Remove g_convert (moved to glib) and now useless utf_to_latin1()
authorOwen Taylor <otaylor@redhat.com>
Thu, 14 Sep 2000 16:41:20 +0000 (16:41 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Thu, 14 Sep 2000 16:41:20 +0000 (16:41 +0000)
Thu Sep 14 12:21:12 2000  Owen Taylor  <otaylor@redhat.com>

        * gtk/gtktexttypes.[ch]: Remove g_convert (moved to
glib) and now useless utf_to_latin1() latin1_to_utf()

* gtk/gtktextview.[ch]: Change ::move_insert and
::delete_text action signals to ::move and ::delete;
create the signals with the right enumeration type,
not GTK_TYPE_ENUM so that bindings work. Add C-d, M-d,
C-v bindings, change Home, End to move to beginning/end
of line, Add C-Home C-End to move to beginning/end
of buffer. Change ::cut_text to ::cut_clipboard, etc;
combine ::scroll_text into ::move; use new GtkSelectionData
functions to simplify DND text handling.

* gtk/gtkenums.h gtk/gtktextview.h: Move movement,
deletion enumerations here, rename enumeration values to
be consistently plural.

* gtk/gtktextbuffer.c: Use new clipboard interfaces
for cut/copy/paste and primary selection.

* gtk/gtktextbuffer.[ch]: Remove excess time and
'interactive' arguments from cut/copy/paste;
rename cut to cut_clipboard, etc; remove
gtk_text_buffer_get_clipboard_contents().

* gtk/gtktextlayout.[ch]: Add
gtk_text_layout_move_iter_to_line_end() to move the iter to
line ends.

* gtk/gtkselection.[ch] (gtk_selection_data_set/get_text):
Functions to set or get a UTF-8 string on the selection
data.

* gtk/gtkclipboard.[ch]: New, simplified selection handling
interfaces.

* gtk/gtkinvisible.c (gtk_invisible_new): Realize newly
created widgets - one of these is useless if we don't.

* gtk/gtkselection.[ch] (gtk_selection_clear_targets): Export
a public function clear all targets registered for the
widget.

* gtk/gtkselection.c (gtk_selection_owner_set) docs/Changes-2.0.txt:
Never call gtk_widget_realize() - that was just asking
for bizarre side-effects.

* gtk/gtkselection.c (gtk_selection_owner_set): Call
gdk_selection_owner_set even if the widget is the
same so that we reliably update the timestamp on
the server.

* gdk/x11/gdkevents-x11.c gdk/x11/gdkx.h: Add a
gdk_x11_get_server_time() function.

* gdk/x11/gdkevents-x11.c gdk/x11/gdkprivate-x11.h
gdk/x11/gdkselection-x11.c gdk/x11/gdkwindow-x11.h:
Add some tricky filtering on serial numbers for
selection clear events to fix up long-standard
race condition FIXME's in gtkselection.c.

* gdk/gdkproperty.h gdk/x11/gdkselection-x11.h: Add
routines to convert from utf8 to compound text or
STRING and from a text property to UTF-8.

* gtk/gtkmain.[ch] (gtk_get_current_event_time): Add
a convenience function gdk_get_current_event_time().

* gtk/gtkselection.c (gtk_selection_data_copy/free): Copy
and free selection_data->data properly

20 files changed:
gdk/gdkproperty.h
gdk/x11/gdkevents-x11.c
gdk/x11/gdkprivate-x11.h
gdk/x11/gdkselection-x11.c
gdk/x11/gdkwindow-x11.c
gdk/x11/gdkx.h
gtk/gtkclipboard.c [new file with mode: 0644]
gtk/gtkclipboard.h [new file with mode: 0644]
gtk/gtkenums.h
gtk/gtkinvisible.c
gtk/gtkmain.c
gtk/gtkmain.h
gtk/gtkselection.c
gtk/gtkselection.h
gtk/gtktextbuffer.c
gtk/gtktextbuffer.h
gtk/gtktextlayout.c
gtk/gtktextlayout.h
gtk/gtktextview.c
gtk/gtktextview.h

index 2bb9770229b8b9eec5d14eaff2d836b357b8bae8..2255d30d3c4bb71ae1163a8f8af127651482cad6 100644 (file)
@@ -43,6 +43,19 @@ gint gdk_text_property_to_text_list (GdkAtom        encoding,
                                     const guchar  *text,
                                     gint           length,
                                     gchar       ***list);
+gint gdk_text_property_to_utf8_list (GdkAtom        encoding,
+                                    gint           format,
+                                    const guchar  *text,
+                                    gint           length,
+                                    gchar       ***list);
+  
+gchar   *gdk_utf8_to_string_target   (const gchar *str);
+gboolean gdk_utf8_to_compound_text (const gchar *str,
+                                   GdkAtom     *encoding,
+                                   gint        *format,
+                                   guchar     **ctext,
+                                   gint        *length);
+
 void gdk_free_text_list             (gchar        **list);
 gint gdk_string_to_compound_text    (const gchar   *str,
                                     GdkAtom       *encoding,
index a389dc4afa58d6adf71c1d34673a42053ef59e29..cb1e1071126249ea6d01f8e2651b80fce6e7765d 100644 (file)
@@ -1059,12 +1059,17 @@ gdk_event_translate (GdkEvent *event,
       GDK_NOTE (EVENTS,
                g_message ("selection clear:\twindow: %ld",
                           xevent->xproperty.window));
-      
-      event->selection.type = GDK_SELECTION_CLEAR;
-      event->selection.window = window;
-      event->selection.selection = xevent->xselectionclear.selection;
-      event->selection.time = xevent->xselectionclear.time;
-      
+
+      if (_gdk_selection_filter_clear_event (&xevent->xselectionclear))
+       {
+         event->selection.type = GDK_SELECTION_CLEAR;
+         event->selection.window = window;
+         event->selection.selection = xevent->xselectionclear.selection;
+         event->selection.time = xevent->xselectionclear.time;
+       }
+      else
+       return_val = FALSE;
+         
       break;
       
     case SelectionRequest:
@@ -1494,4 +1499,57 @@ gdk_flush (void)
   XSync (gdk_display, False);
 }
 
+static GdkAtom timestamp_prop_atom = 0;
+
+static Bool
+timestamp_predicate (Display *display,
+                    XEvent  *xevent,
+                    XPointer arg)
+{
+  Window xwindow = GPOINTER_TO_UINT (arg);
+
+  if (xevent->type == PropertyNotify &&
+      xevent->xproperty.window == xwindow &&
+      xevent->xproperty.atom == timestamp_prop_atom)
+    return True;
+
+  return False;
+}
+
+/**
+ * gdk_x11_get_server_time:
+ * @window: a #GdkWindow, used for communication with the server.
+ *          The window must have GDK_PROPERTY_CHANGE_MASK in its
+ *          events mask or a hang will result.
+ * 
+ * Routine to get the current X server time stamp. 
+ * 
+ * Return value: the time stamp.
+ **/
+guint32
+gdk_x11_get_server_time (GdkWindow *window)
+{
+  Display *xdisplay;
+  Window   xwindow;
+  guchar c = 'a';
+  XEvent xevent;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
+  g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), 0);
+
+  if (!timestamp_prop_atom)
+    timestamp_prop_atom = gdk_atom_intern ("GDK_TIMESTAMP_PROP", FALSE);
+
+  xdisplay = GDK_WINDOW_XDISPLAY (window);
+  xwindow = GDK_WINDOW_XWINDOW (window);
+  
+  XChangeProperty (xdisplay, xwindow,
+                  timestamp_prop_atom, timestamp_prop_atom,
+                  8, PropModeReplace, &c, 1);
+
+  XIfEvent (xdisplay, &xevent,
+           timestamp_predicate, GUINT_TO_POINTER(xwindow));
+
+  return xevent.xproperty.time;
+}
 
index a5214126222f1fce480ad11b06ec0aed8e334124..8a600ee694a49c97cea24ffd741cdd89b3c41850 100644 (file)
@@ -73,13 +73,15 @@ void _gdk_window_process_expose    (GdkWindow     *window,
                                     gulong         serial,
                                     GdkRectangle  *area);
 
+void     _gdk_selection_window_destroyed   (GdkWindow            *window);
+gboolean _gdk_selection_filter_clear_event (XSelectionClearEvent *event);
+
 extern GdkDrawableClass  _gdk_x11_drawable_class;
 extern gboolean                 gdk_use_xshm;
 extern Atom             gdk_wm_delete_window;
 extern Atom             gdk_wm_take_focus;
 extern Atom             gdk_wm_protocols;
 extern Atom             gdk_wm_window_protocols[];
-extern GdkWindow       *selection_owner[];
 extern gboolean          gdk_null_window_warnings;
 extern const int         gdk_nevent_masks;
 extern const int         gdk_event_mask_table[];
@@ -95,5 +97,3 @@ extern GdkWindow *gdk_xim_window;             /* currently using Window */
 
 
 #endif /* __GDK_PRIVATE_X11_H__ */
-
-
index 232dcf63942d379678dbeaf3a89a54e367ec6dd2..1879226788d5e43a79f34a207d145d028c1dfc85 100644 (file)
 #include "gdkprivate.h"
 #include "gdkprivate-x11.h"
 
+typedef struct _OwnerInfo OwnerInfo;
+
+struct _OwnerInfo
+{
+  GdkAtom    selection;
+  GdkWindow *owner;
+  gulong     serial;
+};
+
+GSList *owner_list;
+
+/* When a window is destroyed we check if it is the owner
+ * of any selections. This is somewhat inefficient, but
+ * owner_list is typically short, and it is a low memory,
+ * low code solution
+ */
+void
+_gdk_selection_window_destroyed (GdkWindow *window)
+{
+  GSList *tmp_list = owner_list;
+  while (tmp_list)
+    {
+      OwnerInfo *info = tmp_list->data;
+      if (info->owner == window)
+       {
+         owner_list = g_slist_remove (owner_list, info);
+         g_free (info);
+       }
+      tmp_list = tmp_list->next;
+    }
+}
+
+/* We only pass through those SelectionClear events that actually
+ * reflect changes to the selection owner that we didn't make ourself.
+ */
+gboolean
+_gdk_selection_filter_clear_event (XSelectionClearEvent *event)
+{
+  GSList *tmp_list = owner_list;
+
+  while (tmp_list)
+    {
+      OwnerInfo *info = tmp_list->data;
+      if (info->selection == event->selection)
+       {
+         if ((GDK_DRAWABLE_XID (info->owner) == event->window &&
+              event->serial >= info->serial))
+           {
+             owner_list = g_slist_remove (owner_list, info);
+             g_free (info);
+             return TRUE;
+           }
+         else
+           return FALSE;
+       }
+      tmp_list = tmp_list->next;
+    }
+
+  return FALSE;
+}
 
 gboolean
 gdk_selection_owner_set (GdkWindow *owner,
@@ -42,6 +102,8 @@ gdk_selection_owner_set (GdkWindow *owner,
 {
   Display *xdisplay;
   Window xwindow;
+  GSList *tmp_list;
+  OwnerInfo *info;
 
   if (owner)
     {
@@ -56,6 +118,29 @@ gdk_selection_owner_set (GdkWindow *owner,
       xdisplay = gdk_display;
       xwindow = None;
     }
+  
+  tmp_list = owner_list;
+  while (tmp_list)
+    {
+      info = tmp_list->data;
+      if (info->selection == selection)
+       {
+         owner_list = g_slist_remove (owner_list, info);
+         g_free (info);
+         break;
+       }
+      tmp_list = tmp_list->next;
+    }
+
+  if (owner)
+    {
+      info = g_new (OwnerInfo, 1);
+      info->owner = owner;
+      info->serial = NextRequest (GDK_WINDOW_XDISPLAY (owner));
+      info->selection = selection;
+
+      owner_list = g_slist_prepend (owner_list, info);
+    }
 
   XSetSelectionOwner (xdisplay, selection, xwindow, time);
 
@@ -221,6 +306,166 @@ gdk_free_text_list (gchar **list)
   XFreeStringList (list);
 }
 
+static gint
+make_list (const gchar  *text,
+          gint          length,
+          gboolean      latin1,
+          gchar      ***list)
+{
+  GSList *strings = NULL;
+  gint n_strings = 0;
+  gint i;
+  const gchar *p = text;
+  const gchar *q;
+  GSList *tmp_list;
+  GError *error = NULL;
+
+  while (p < text + length)
+    {
+      gchar *str;
+      
+      q = p;
+      while (*q && q < text + length)
+       q++;
+
+      if (latin1)
+       {
+         str = g_convert (p, q - p,
+                          "UTF-8", "ISO-8859-1",
+                          NULL, NULL, &error);
+
+         if (!str)
+           {
+             g_warning ("Error converting selection from STRING: %s",
+                        error->message);
+             g_error_free (error);
+           }
+       }
+      else
+       str = g_strndup (p, q - p);
+
+      if (str)
+       {
+         strings = g_slist_prepend (strings, str);
+         n_strings++;
+       }
+
+      p = q + 1;
+    }
+
+  if (list)
+    *list = g_new (gchar *, n_strings + 1);
+
+  (*list)[n_strings] = NULL;
+  
+  i = n_strings;
+  tmp_list = strings;
+  while (tmp_list)
+    {
+      if (list)
+       (*list)[--i] = tmp_list->data;
+      else
+       g_free (tmp_list->data);
+
+      tmp_list = tmp_list->next;
+    }
+
+  g_slist_free (strings);
+
+  return n_strings;
+}
+
+/**
+ * gdk_text_property_to_utf8_list:
+ * @encoding: an atom representing the encoding of the text
+ * @format:   the format of the property
+ * @text:     the text to convert
+ * @length:   the length of @text, in bytes
+ * @list:     location to store the list of strings or %NULL. The
+ *            list should be freed with g_strfreev().
+ * 
+ * Convert a text property in the giving encoding to
+ * a list of UTF-8 strings. 
+ * 
+ * Return value: the number of strings in the resulting
+ *               list.
+ **/
+gint 
+gdk_text_property_to_utf8_list (GdkAtom        encoding,
+                               gint           format,
+                               const guchar  *text,
+                               gint           length,
+                               gchar       ***list)
+{
+  g_return_val_if_fail (text != NULL, 0);
+  g_return_val_if_fail (length >= 0, 0);
+  
+  if (encoding == GDK_TARGET_STRING)
+    {
+      return make_list ((gchar *)text, length, TRUE, list);
+    }
+  else if (encoding == gdk_atom_intern ("UTF8_STRING", FALSE))
+    {
+      return make_list ((gchar *)text, length, FALSE, list);
+    }
+  else
+    {
+      gchar **local_list;
+      gint local_count;
+      gint i;
+      gchar *charset = NULL;
+      gboolean need_conversion= g_get_charset (&charset);
+      gint count = 0;
+      GError *error = NULL;
+      
+      /* Probably COMPOUND text, we fall back to Xlib routines
+       */
+      local_count = gdk_text_property_to_text_list (encoding,
+                                                   format, 
+                                                   text,
+                                                   length,
+                                                   &local_list);
+      if (list)
+       *list = g_new (gchar *, local_count + 1);
+      
+      for (i=0; i<local_count; i++)
+       {
+         /* list contains stuff in our default encoding
+          */
+         if (need_conversion)
+           {
+             gchar *utf = g_convert (local_list[i], -1,
+                                     "UTF-8", charset,
+                                     NULL, NULL, &error);
+             if (utf)
+               {
+                 if (list)
+                   (*list)[count++] = utf;
+                 else
+                   g_free (utf);
+               }
+             else
+               {
+                 g_warning ("Error converting to UTF-8 from '%s': %s",
+                            charset, error->message);
+                 g_error_free (error);
+                 error = NULL;
+               }
+           }
+         else
+           {
+             if (list)
+               (*list)[count++] = g_strdup (local_list[i]);
+           }
+       }
+      
+      gdk_free_text_list (local_list);
+      (*list)[count] = NULL;
+
+      return count;
+    }
+}
+
 gint
 gdk_string_to_compound_text (const gchar *str,
                             GdkAtom     *encoding,
@@ -254,6 +499,151 @@ gdk_string_to_compound_text (const gchar *str,
   return res;
 }
 
+/* The specifications for COMPOUND_TEXT and STRING specify that C0 and
+ * C1 are not allowed except for \n and \t, however the X conversions
+ * routines for COMPOUND_TEXT only enforce this in one direction,
+ * causing cut-and-paste of \r and \r\n separated text to fail.
+ * This routine strips out all non-allowed C0 and C1 characters
+ * from the input string and also canonicalizes \r, \r\n, and \n\r to \n
+ */
+static gchar * 
+sanitize_utf8 (const gchar *src)
+{
+  gint len = strlen (src);
+  GString *result = g_string_sized_new (len);
+  const gchar *p = src;
+
+  while (*p)
+    {
+      if (*p == '\r' || *p == '\n')
+       {
+         p++;
+         if (*p == '\r' || *p == '\n')
+           p++;
+
+         g_string_append_c (result, '\n');
+       }
+      else
+       {
+         gunichar ch = g_utf8_get_char (p);
+         char buf[7];
+         gint buflen;
+         
+         if (!((ch < 0x20 && ch != '\t') || (ch >= 0x7f && ch < 0xa0)))
+           {
+             buflen = g_unichar_to_utf8 (ch, buf);
+             g_string_append_len (result, buf, buflen);
+           }
+
+         p = g_utf8_next_char (p);
+       }
+    }
+
+  return g_string_free (result, FALSE);
+}
+
+/**
+ * gdk_utf8_to_string_target:
+ * @str: a UTF-8 string
+ * 
+ * Convert an UTF-8 string into the best possible representation
+ * as a STRING. The representation of characters not in STRING
+ * is not specified; it may be as pseudo-escape sequences
+ * \x{ABCD}, or it may be in some other form of approximation.
+ * 
+ * Return value: the newly allocated string, or %NULL if the
+ *               conversion failed. (It should not fail for
+ *               any properly formed UTF-8 string.)
+ **/
+gchar *
+gdk_utf8_to_string_target (const gchar *str)
+{
+  GError *error = NULL;
+  
+  gchar *tmp_str = sanitize_utf8 (str);
+  gchar *result =  g_convert_with_fallback (tmp_str, -1,
+                                           "ISO-8859-1", "UTF-8",
+                                           NULL, NULL, NULL, &error);
+  if (!result)
+    {
+      g_warning ("Error converting from UTF-8 to STRING: %s",
+                error->message);
+      g_error_free (error);
+    }
+  
+  g_free (tmp_str);
+  return result;
+}
+
+/**
+ * gdk_utf8_to_compound_text:
+ * @str:      a UTF-8 string
+ * @encoding: location to store resulting encoding
+ * @format:   location to store format of the result
+ * @ctext:    location to store the data of the result
+ * @length:   location to store the length of the data
+ *            stored in @ctext
+ * 
+ * Convert from UTF-8 to compound text. 
+ * 
+ * Return value: %TRUE if the conversion succeeded, otherwise
+ *               false.
+ **/
+gboolean
+gdk_utf8_to_compound_text (const gchar *str,
+                          GdkAtom     *encoding,
+                          gint        *format,
+                          guchar     **ctext,
+                          gint        *length)
+{
+  gboolean need_conversion;
+  gchar *charset;
+  gchar *locale_str, *tmp_str;
+  GError *error = NULL;
+  gboolean result;
+
+  g_return_val_if_fail (str != NULL, FALSE);
+
+  need_conversion = g_get_charset (&charset);
+
+  tmp_str = sanitize_utf8 (str);
+
+  if (need_conversion)
+    {
+      locale_str = g_convert_with_fallback (tmp_str, -1,
+                                           charset, "UTF-8",
+                                           NULL, NULL, NULL, &error);
+      g_free (tmp_str);
+
+      if (!locale_str)
+       {
+         g_warning ("Error converting from UTF-8 to '%s': %s",
+                    charset, error->message);
+         g_error_free (error);
+
+         if (encoding)
+           *encoding = None;
+         if (format)
+           *format = None;
+         if (ctext)
+           *ctext = NULL;
+         if (length)
+           *length = 0;
+
+         return FALSE;
+       }
+    }
+  else
+    locale_str = tmp_str;
+    
+  result = gdk_string_to_compound_text (locale_str,
+                                       encoding, format, ctext, length);
+  
+  g_free (locale_str);
+
+  return result;
+}
+
 void gdk_free_compound_text (guchar *ctext)
 {
   if (ctext)
index d4741a4f72bc430a358a15fe3fe8e290868dce6c..761782d9f06e1050355e98946b1fafafeda949eb 100644 (file)
@@ -630,6 +630,8 @@ _gdk_windowing_window_destroy (GdkWindow *window,
   GdkWindowObject *private = (GdkWindowObject *)window;
 
   g_return_if_fail (GDK_IS_WINDOW (window));
+
+  _gdk_selection_window_destroyed (window);
   
   if (private->extension_events != 0)
     gdk_input_window_destroy (window);
index 72c7d72bd2e976d30a07a32b13939c7b18b85df3..697afea9adbbe45f6274ba989944d02d1b02aa3d 100644 (file)
@@ -178,6 +178,8 @@ GdkWindow    *gdk_window_foreign_new (GdkNativeWindow anid);
 /* Return the Gdk* for a particular XID */
 gpointer      gdk_xid_table_lookup     (XID              xid);
 
+guint32       gdk_x11_get_server_time  (GdkWindow       *window);
+
 #define gdk_window_lookup(xid)    ((GdkWindow*) gdk_xid_table_lookup (xid))
 #define gdk_pixmap_lookup(xid)    ((GdkPixmap*) gdk_xid_table_lookup (xid))
 #define gdk_font_lookup(xid)      ((GdkFont*) gdk_xid_table_lookup (xid))
diff --git a/gtk/gtkclipboard.c b/gtk/gtkclipboard.c
new file mode 100644 (file)
index 0000000..6df1843
--- /dev/null
@@ -0,0 +1,806 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Global clipboard abstraction. 
+ */
+
+#include <string.h>
+
+#include "gtkclipboard.h"
+#include "gtkinvisible.h"
+#include "gtkmain.h"
+#include "gtksignal.h"
+
+#ifdef GDK_WINDOWING_X11
+#include "x11/gdkx.h"
+#endif
+
+typedef struct _RequestContentsInfo RequestContentsInfo;
+typedef struct _RequestTextInfo RequestTextInfo;
+
+struct _GtkClipboard 
+{
+  GdkAtom selection;
+
+  GtkClipboardGetFunc get_func;
+  GtkClipboardClearFunc clear_func;
+  gpointer user_data;
+  gboolean have_owner;
+
+  guint32 timestamp;
+
+  gboolean have_selection;
+};
+
+struct _RequestContentsInfo
+{
+  GtkClipboardReceivedFunc callback;
+  gpointer user_data;
+};
+
+struct _RequestTextInfo
+{
+  GtkClipboardTextReceivedFunc callback;
+  gpointer user_data;
+};
+
+static void clipboard_unset    (GtkClipboard     *clipboard);
+static void selection_received (GtkWidget        *widget,
+                               GtkSelectionData *selection_data,
+                               guint             time);
+
+static GSList *clipboards;
+static GtkWidget *clipboard_widget;
+
+enum {
+  TARGET_STRING,
+  TARGET_TEXT,
+  TARGET_COMPOUND_TEXT,
+  TARGET_UTF8_STRING
+};
+
+static const gchar *request_contents_key = "gtk-request-contents";
+static GQuark request_contents_key_id = 0;
+
+static const gchar *clipboards_owned_key = "gtk-clipboards-owned";
+static GQuark clipboards_owned_key_id = 0;
+  
+
+/**
+ * gtk_clipboard_get:
+ * @selection: a #GdkAtom which identifies the clipboard
+ *             to use. A value of GDK_NONE here is the
+ *             same as gdk_atom_intern ("CLIPBOARD", FALSE),
+ *             and provides the default clipboard. Another
+ *             common value is GDK_SELECTION_PRIMARY, which
+ *             identifies the primary X selection. 
+ * 
+ * Returns the clipboard object for the given selection.
+ * 
+ * Return value: the appropriate clipboard object. If no
+ *             clipboard already exists, a new one will
+ *             be created. Once a clipboard object has
+ *             been created, it is persistant for all time.
+ **/
+GtkClipboard *
+gtk_clipboard_get (GdkAtom selection)
+{
+  GtkClipboard *clipboard = NULL;
+  GSList *tmp_list;
+
+  if (selection == GDK_NONE)
+    selection = gdk_atom_intern ("CLIPBOARD", FALSE);
+
+  tmp_list = clipboards;
+  while (tmp_list)
+    {
+      clipboard = tmp_list->data;
+      if (clipboard->selection == selection)
+       break;
+
+      tmp_list = tmp_list->next;
+    }
+
+  if (!tmp_list)
+    {
+      clipboard = g_new0 (GtkClipboard, 1);
+      clipboard->selection = selection;
+      clipboards = g_slist_prepend (clipboards, clipboard);
+    }
+  
+  return clipboard;
+}
+
+static void 
+selection_get_cb (GtkWidget          *widget,
+                 GtkSelectionData   *selection_data,
+                 guint               time,
+                 guint               info)
+{
+  GtkClipboard *clipboard = gtk_clipboard_get (selection_data->selection);
+
+  if (clipboard && clipboard->get_func)
+    clipboard->get_func (clipboard, selection_data, info, clipboard->user_data);
+}
+
+static gboolean
+selection_clear_event_cb (GtkWidget        *widget,
+                         GdkEventSelection *event)
+{
+  GtkClipboard *clipboard = gtk_clipboard_get (event->selection);
+
+  if (clipboard)
+    {
+      clipboard_unset (clipboard);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+GtkWidget *
+make_clipboard_widget (gboolean provider)
+{
+  GtkWidget *widget = gtk_invisible_new ();
+
+  gtk_signal_connect (GTK_OBJECT (widget), "selection_received",
+                     GTK_SIGNAL_FUNC (selection_received), NULL);
+
+  if (provider)
+    {
+      /* We need this for gdk_x11_get_server_time() */
+      gtk_widget_add_events (widget, GDK_PROPERTY_CHANGE_MASK);
+      
+      gtk_signal_connect (GTK_OBJECT (widget), "selection_get",
+                         GTK_SIGNAL_FUNC (selection_get_cb), NULL);
+      gtk_signal_connect (GTK_OBJECT (widget), "selection_clear_event",
+                         GTK_SIGNAL_FUNC (selection_clear_event_cb), NULL);
+    }
+
+  return widget;
+}
+
+
+void
+ensure_clipboard_widget ()
+{
+  if (!clipboard_widget)
+    clipboard_widget = make_clipboard_widget (TRUE);
+}
+
+/* This function makes a very good guess at what the correct
+ * timestamp for a selection request should be. If there is
+ * a currently processed event, it uses the timestamp for that
+ * event, otherwise it uses the current server time. However,
+ * if the time resulting from that is older than the time used
+ * last time, it uses the time used last time instead.
+ *
+ * In order implement this correctly, we never use CurrentTime,
+ * but actually retrieve the actual timestamp from the server.
+ * This is a little slower but allows us to make the guarantee
+ * that the times used by this application will always ascend
+ * and we won't get selections being rejected just because
+ * we are using a correct timestamp from an event, but used
+ * CurrentTime previously.
+ */
+static guint32
+clipboard_get_timestamp (GtkClipboard *clipboard)
+{
+  guint32 timestamp = gtk_get_current_event_time ();
+
+  ensure_clipboard_widget ();
+  
+  if (timestamp == GDK_CURRENT_TIME)
+    {
+      timestamp = gdk_x11_get_server_time (clipboard_widget->window);
+    }
+  else
+    {
+      if (clipboard->timestamp != GDK_CURRENT_TIME)
+       {
+         /* Check to see if clipboard->timestamp is newer than
+          * timestamp, accounting for wraparound.
+          */
+
+         guint32 max = timestamp + 0x80000000;
+
+         if ((max > timestamp &&
+              (clipboard->timestamp > timestamp &&
+               clipboard->timestamp <= max)) ||
+             (max <= timestamp &&
+              (clipboard->timestamp > timestamp ||
+               clipboard->timestamp <= max)))
+           {
+             timestamp = clipboard->timestamp;
+           }
+       }
+    }
+
+  clipboard->timestamp = timestamp;
+
+  return timestamp;
+}
+
+static void
+clipboard_owner_destroyed (gpointer data)
+{
+  GSList *clipboards = data;
+  GSList *tmp_list;
+
+  tmp_list = clipboards;
+  while (tmp_list)
+    {
+      GtkClipboard *clipboard = tmp_list->data;
+
+      clipboard->get_func = NULL;
+      clipboard->clear_func = NULL;
+      clipboard->user_data = NULL;
+      clipboard->have_owner = FALSE;
+
+      gtk_clipboard_clear (clipboard);
+
+      tmp_list = tmp_list->next;
+    }
+  
+  g_slist_free (clipboards);
+}
+
+static void
+clipboard_add_owner_notify (GtkClipboard *clipboard)
+{
+  if (!clipboards_owned_key_id)
+    clipboards_owned_key_id = g_quark_from_static_string (clipboards_owned_key);
+  
+  if (clipboard->have_owner)
+    g_object_set_qdata_full (clipboard->user_data, clipboards_owned_key_id,
+                            g_slist_prepend (g_object_steal_qdata (clipboard->user_data,
+                                                                   clipboards_owned_key_id),
+                                             clipboard),
+                            clipboard_owner_destroyed);
+}
+
+static void
+clipboard_remove_owner_notify (GtkClipboard *clipboard)
+{
+  if (clipboard->have_owner)
+     g_object_set_qdata_full (clipboard->user_data, clipboards_owned_key_id,
+                             g_slist_remove (g_object_steal_qdata (clipboard->user_data,
+                                                                   clipboards_owned_key_id),
+                                             clipboard),
+                             clipboard_owner_destroyed);
+}
+         
+static gboolean
+gtk_clipboard_set_contents (GtkClipboard         *clipboard,
+                           const GtkTargetEntry *targets,
+                           guint                 n_targets,
+                           GtkClipboardGetFunc   get_func,
+                           GtkClipboardClearFunc clear_func,
+                           gpointer              user_data,
+                           gboolean              have_owner)
+{
+  ensure_clipboard_widget ();
+
+  if (gtk_selection_owner_set (clipboard_widget, clipboard->selection,
+                              clipboard_get_timestamp (clipboard)))
+    {
+      clipboard->have_selection = TRUE;
+
+      if (!(clipboard->have_owner && have_owner) ||
+         clipboard->user_data != user_data)
+       {
+         clipboard_unset (clipboard);
+
+         if (clipboard->get_func)
+           {
+             /* Calling unset() caused the clipboard contents to be reset!
+              * Avoid leaking and return 
+              */
+             if (!(clipboard->have_owner && have_owner) ||
+                 clipboard->user_data != user_data)
+               {
+                 (*clear_func) (clipboard, user_data);
+                 return FALSE;
+               }
+             else
+               return TRUE;
+           }
+         else
+           {
+             clipboard->user_data = user_data;
+             clipboard->have_owner = have_owner;
+             if (have_owner)
+               clipboard_add_owner_notify (clipboard);
+           }
+         
+       }
+
+      clipboard->get_func = get_func;
+      clipboard->clear_func = clear_func;
+
+      gtk_selection_clear_targets (clipboard_widget, clipboard->selection);
+      gtk_selection_add_targets (clipboard_widget, clipboard->selection,
+                                targets, n_targets);
+
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+/**
+ * gtk_clipboard_set_with_data:
+ * @clipboard:  a #GtkClipboard
+ * @targets:    array containing information about the available forms for the
+ *              clipboard data
+ * @n_targets:  number of elements in @targets
+ * @get_func:   function to call to get the actual clipboard data
+ * @clear_func: when the clipboard contents are set again, this function will
+ *              be called, and get_func will not be subsequently called.
+ * @user_data:  user data to pass to @get_func and @clear_func.
+ * 
+ * Virtually set the contents of the specified clipboard by providing
+ * a list of supported formats for the clipboard data and a function
+ * to call to get the actual data when it is requested.
+ * 
+ * Return value: %TRUE if setting the clipboard data succeeded. If setting
+ *               the clipboard data failed the provided callback functions
+ *               will be ignored.
+ **/
+gboolean
+gtk_clipboard_set_with_data (GtkClipboard          *clipboard,
+                            const GtkTargetEntry  *targets,
+                            guint                  n_targets,
+                            GtkClipboardGetFunc    get_func,
+                            GtkClipboardClearFunc  clear_func,
+                            gpointer               user_data)
+{
+  g_return_val_if_fail (clipboard != NULL, FALSE);
+  g_return_val_if_fail (targets != NULL, FALSE);
+  g_return_val_if_fail (get_func != NULL, FALSE);
+
+  return gtk_clipboard_set_contents (clipboard, targets, n_targets,
+                                    get_func, clear_func, user_data,
+                                    FALSE);
+}
+
+/**
+ * gtk_clipboard_set_with_owner:
+ * @clipboard:  a #GtkClipboard
+ * @targets:    array containing information about the available forms for the
+ *              clipboard data
+ * @n_targets:  number of elements in @targets
+ * @get_func:   function to call to get the actual clipboard data
+ * @clear_func: when the clipboard contents are set again, this function will
+ *              be called, and get_func will not be subsequently called.
+ * @owner:      an object that "owns" the data. This object will be passed
+ *              to the callbacks when called. 
+ * 
+ * Virtually set the contents of the specified clipboard by providing
+ * a list of supported formats for the clipboard data and a function
+ * to call to get the actual data when it is requested.
+ *
+ * The difference between this function and gtk_clipboard_set_with_data
+ * is that instead of an generic @user_data pointer, a #GObject is passed
+ * in. Because of this, 
+ * 
+ * Return value: %TRUE if setting the clipboard data succeeded. If setting
+ *               the clipboard data failed the provided callback functions
+ *               will be ignored.
+ **/
+gboolean
+gtk_clipboard_set_with_owner (GtkClipboard          *clipboard,
+                             const GtkTargetEntry  *targets,
+                             guint                  n_targets,
+                             GtkClipboardGetFunc    get_func,
+                             GtkClipboardClearFunc  clear_func,
+                             GObject               *owner)
+{
+  g_return_val_if_fail (clipboard != NULL, FALSE);
+  g_return_val_if_fail (targets != NULL, FALSE);
+  g_return_val_if_fail (get_func != NULL, FALSE);
+  g_return_val_if_fail (G_IS_OBJECT (owner), FALSE);
+
+  return gtk_clipboard_set_contents (clipboard, targets, n_targets,
+                                    get_func, clear_func, owner,
+                                    TRUE);
+}
+
+/**
+ * gtk_clipboard_get_owner:
+ * @clipboard: a #GtkClipboard
+ * 
+ * If the clipboard contents callbacks were set with gtk_clipboard_set_owner(),
+ * and the gtk_clipboard_set_with_data() or gtk_clipboard_clear() has not
+ * subsequently called, returns the @owner set by gtk_clipboard_set_owner().
+ * 
+ * Return value: the owner of the clipboard, if any; otherwise %NULL.
+ **/
+GObject *
+gtk_clipboard_get_owner (GtkClipboard *clipboard)
+{
+  g_return_val_if_fail (clipboard != NULL, NULL);
+
+  if (clipboard->have_owner)
+    return clipboard->user_data;
+  else
+    return NULL;
+}
+
+static void
+clipboard_unset (GtkClipboard *clipboard)
+{
+  GtkClipboardClearFunc old_clear_func;
+  gpointer old_data;
+  
+  old_clear_func = clipboard->clear_func;
+  old_data = clipboard->user_data;
+         
+  if (clipboard->have_owner)
+    {
+      clipboard_remove_owner_notify (clipboard);
+      clipboard->have_owner = FALSE;
+    }
+  
+  clipboard->get_func = NULL;
+  clipboard->clear_func = NULL;
+  clipboard->user_data = NULL;
+  
+  if (old_clear_func)
+    old_clear_func (clipboard, old_data);
+}
+
+/**
+ * gtk_clipboard_clear:
+ * @clipboard:  a #GtkClipboard
+ * 
+ * Clear the contents of the clipboard. Generally this should only
+ * be called between the time you call gtk_clipboard_set_contents(),
+ * and when the @clear_func you supplied is called. Otherwise, the
+ * clipboard may be owned by someone else.
+ **/
+void
+gtk_clipboard_clear (GtkClipboard *clipboard)
+{
+  g_return_if_fail (clipboard != NULL);
+
+  if (clipboard->have_selection)
+    gtk_selection_owner_set (NULL, clipboard->selection,
+                            clipboard_get_timestamp (clipboard));
+}
+
+void 
+text_get_func (GtkClipboard     *clipboard,
+              GtkSelectionData *selection_data,
+              guint             info,
+              gpointer          data)
+{
+  gtk_selection_data_set_text (selection_data, data);
+}
+
+void 
+text_clear_func (GtkClipboard *clipboard,
+                gpointer      data)
+{
+  g_free (data);
+}
+
+/**
+ * gtk_clipboard_set_text:
+ * @clipboard: a #GtkClipboard object
+ * @text:      a UTF-8 string.
+ * @len:       length of @text, in bytes, or -1, in which case
+ *             the length will be determined with strlen().
+ * 
+ * Set the contents of the clipboard to the given UTF-8 string. GTK+ will
+ * make a copy of the text and take responsibility for responding
+ * for requests for the text, and for converting the text into
+ * the requested format.
+ **/
+void 
+gtk_clipboard_set_text (GtkClipboard *clipboard,
+                       const gchar  *text,
+                       gint          len)
+{
+  static const GtkTargetEntry targets[] = {
+    { "STRING", 0, TARGET_STRING },
+    { "TEXT",   0, TARGET_TEXT }, 
+    { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
+    { "UTF8_STRING", 0, TARGET_UTF8_STRING }
+  };
+
+  g_return_if_fail (clipboard != NULL);
+  g_return_if_fail (text != NULL);
+  
+  if (len < 0)
+    len = strlen (text);
+  
+  gtk_clipboard_set_with_data (clipboard, 
+                              targets, G_N_ELEMENTS (targets),
+                              text_get_func, text_clear_func,
+                              g_strndup (text, len));
+}
+
+static void
+set_request_contents_info (GtkWidget           *widget,
+                          RequestContentsInfo *info)
+{
+  if (!request_contents_key_id)
+    request_contents_key_id = g_quark_from_static_string (request_contents_key);
+
+  gtk_object_set_data_by_id (GTK_OBJECT (widget),
+                            request_contents_key_id,
+                            info);
+}
+
+static RequestContentsInfo *
+get_request_contents_info (GtkWidget *widget)
+{
+  if (!request_contents_key_id)
+    return NULL;
+  else
+    return gtk_object_get_data_by_id (GTK_OBJECT (widget),
+                                     request_contents_key_id);
+}
+
+static void 
+selection_received (GtkWidget            *widget,
+                   GtkSelectionData     *selection_data,
+                   guint                 time)
+{
+  RequestContentsInfo *request_info = get_request_contents_info (widget);
+  set_request_contents_info (widget, NULL);
+  
+  request_info->callback (gtk_clipboard_get (selection_data->selection), 
+                         selection_data,
+                         request_info->user_data);
+
+  g_free (request_info);
+
+  if (widget != clipboard_widget)
+    gtk_widget_destroy (widget);
+}
+
+/**
+ * gtk_clipboard_request_contents:
+ * @clipboard: a #GtkClipboard
+ * @target:    an atom representing the form into which the clipboard
+ *             owner should convert the selection.
+ * @callback:  A function to call when the results are received
+ *             (or the retrieval fails.) If the retrieval fails
+ *             the length field of @selection_data will be
+ *             negative.
+ * @user_data: user data to pass to @callback
+ * 
+ * Requests the contents of clipboard as the given target.
+ * When the results of the result are later received the supplied callback
+ * will be called.
+ **/
+void 
+gtk_clipboard_request_contents (GtkClipboard            *clipboard,
+                               GdkAtom                  target,
+                               GtkClipboardReceivedFunc callback,
+                               gpointer                 user_data)
+{
+  RequestContentsInfo *info;
+  GtkWidget *widget;
+
+  g_return_if_fail (clipboard != NULL);
+  g_return_if_fail (target != GDK_NONE);
+  g_return_if_fail (callback != NULL);
+  
+  ensure_clipboard_widget ();
+
+  if (get_request_contents_info (clipboard_widget))
+    widget = make_clipboard_widget (FALSE);
+  else
+    widget = clipboard_widget;
+
+  info = g_new (RequestContentsInfo, 1);
+  info->callback = callback;
+  info->user_data = user_data;
+
+  set_request_contents_info (widget, info);
+
+  gtk_selection_convert (widget, clipboard->selection, target,
+                        clipboard_get_timestamp (clipboard));
+}
+
+static void 
+request_text_received_func (GtkClipboard     *clipboard,
+                           GtkSelectionData *selection_data,
+                           gpointer          data)
+{
+  RequestTextInfo *info = data;
+  gchar *result = NULL;
+
+  result = gtk_selection_data_get_text (selection_data);
+
+  if (!result)
+    {
+      /* If we asked for UTF8 and didn't get it, try text; if we asked
+       * for text and didn't get it, try string.  If we asked for
+       * anything else and didn't get it, give up.
+       */
+      if (selection_data->target == gdk_atom_intern ("UTF8_STRING", FALSE))
+       {
+         gtk_clipboard_request_contents (clipboard,
+                                         gdk_atom_intern ("TEXT", FALSE), 
+                                         request_text_received_func, info);
+         return;
+       }
+      else if (selection_data->target == gdk_atom_intern ("TEXT", FALSE))
+       {
+         gtk_clipboard_request_contents (clipboard,
+                                         GDK_TARGET_STRING, 
+                                         request_text_received_func, info);
+         return;
+       }
+    }
+
+  info->callback (clipboard, result, info->user_data);
+  g_free (info);
+  g_free (result);
+}
+
+/**
+ * gtk_clipboard_request_text:
+ * @clipboard: a #GtkClipboard
+ * @callback:  a function to call when the text is received,
+ *             or the retrieval fails. (It will always be called
+ *             one way or the other.)
+ * @user_data: user data to pass to @callback.
+ * 
+ * Requests the contents of the clipboard as text. When the text is
+ * later received, it will be converted to UTF-8 if necessary, and
+ * @callback will be called. 
+ *
+ * The @text parameter to @callback will contain the resulting text if
+ * the request succeeded, or %NULL if it failed. This could happen for
+ * various reasons, in particular if the clipboard was empty or if the
+ * contents of the clipboard could not be converted into text form.
+ **/
+void 
+gtk_clipboard_request_text (GtkClipboard                *clipboard,
+                           GtkClipboardTextReceivedFunc callback,
+                           gpointer                     user_data)
+{
+  RequestTextInfo *info;
+  
+  g_return_if_fail (clipboard != NULL);
+  g_return_if_fail (callback != NULL);
+  
+  info = g_new (RequestTextInfo, 1);
+  info->callback = callback;
+  info->user_data = user_data;
+
+  gtk_clipboard_request_contents (clipboard, gdk_atom_intern ("UTF8_STRING", FALSE),
+                                 request_text_received_func,
+                                 info);
+}
+
+
+typedef struct
+{
+  GMainLoop *loop;
+  gpointer data;
+} WaitResults;
+
+static void 
+clipboard_received_func (GtkClipboard     *clipboard,
+                        GtkSelectionData *selection_data,
+                        gpointer          data)
+{
+  WaitResults *results = data;
+
+  if (selection_data->length >= 0)
+    results->data = gtk_selection_data_copy (selection_data);
+  
+  g_main_quit (results->loop);
+}
+
+/**
+ * gtk_clipboard_wait_for_contents:
+ * @clipboard: a #GtkClipboard
+ * @target: an atom representing the form into which the clipboard
+ *          owner should convert the selection.
+ * 
+ * Requests the contents of the clipboard using the given target.
+ * This function waits for the data to be received using the main 
+ * loop, so events, timeouts, etc, may be dispatched during the wait.
+ * 
+ * Return value: a newly allocated #GtkSelectionData object or %NULL
+ *               if retrieving the given target failed. If non-%NULL,
+ *               this value freed with gtk_selection_data_free() when
+ *               you are finished with it.
+ **/
+GtkSelectionData *
+gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
+                                GdkAtom       target)
+{
+  WaitResults results;
+
+  g_return_val_if_fail (clipboard != NULL, NULL);
+  g_return_val_if_fail (target != GDK_NONE, NULL);
+  
+  results.data = NULL;
+  results.loop = g_main_new (TRUE);
+
+  gtk_clipboard_request_contents (clipboard, target, 
+                                 clipboard_received_func,
+                                 &results);
+
+  if (g_main_is_running (results.loop))
+    g_main_run (results.loop);
+
+  g_main_destroy (results.loop);
+
+  return results.data;
+}
+
+static void 
+clipboard_text_received_func (GtkClipboard *clipboard,
+                             const gchar  *text,
+                             gpointer      data)
+{
+  WaitResults *results = data;
+
+  results->data = g_strdup (text);
+  g_main_quit (results->loop);
+}
+
+
+/**
+ * gtk_clipboard_wait_for_text:
+ * @clipboard: a #GtkClipboard
+ * 
+ * Requests the contents of the clipboard as text and converts
+ * the result to UTF-8 if necessary. This function waits for
+ * the data to be received using the main loop, so events,
+ * timeouts, etc, may be dispatched during the wait.
+ * 
+ * Return value: a newly allocated UTF-8 string which must
+ *               be freed with g_free(), or %NULL if retrieving
+ *               the selection data failed. (This could happen
+ *               for various reasons, in particular if the
+ *               clipboard was empty or if the contents of the
+ *               clipboard could not be converted into text form.)
+ **/
+gchar *
+gtk_clipboard_wait_for_text (GtkClipboard *clipboard)
+{
+  WaitResults results;
+
+  g_return_val_if_fail (clipboard != NULL, NULL);
+  g_return_val_if_fail (clipboard != NULL, NULL);
+  
+  results.data = NULL;
+  results.loop = g_main_new (TRUE);
+
+  gtk_clipboard_request_text (clipboard,
+                             clipboard_text_received_func,
+                             &results);
+
+  if (g_main_is_running (results.loop))
+    g_main_run (results.loop);
+
+  g_main_destroy (results.loop);
+
+  return results.data;
+}
+
diff --git a/gtk/gtkclipboard.h b/gtk/gtkclipboard.h
new file mode 100644 (file)
index 0000000..f35630e
--- /dev/null
@@ -0,0 +1,87 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Global clipboard abstraction. 
+ */
+
+#ifndef __GTK_CLIPBOARD_H__
+#define __GTK_CLIPBOARD_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gtk/gtkselection.h>
+
+typedef struct _GtkClipboard GtkClipboard;
+
+typedef void (* GtkClipboardReceivedFunc)     (GtkClipboard     *clipboard,
+                                              GtkSelectionData *selection_data,
+                                              gpointer          data);
+typedef void (* GtkClipboardTextReceivedFunc) (GtkClipboard     *clipboard,
+                                              const gchar      *text,
+                                              gpointer          data);
+
+/* Should these functions have GtkClipboard *clipboard as the first argument?
+ * right now for ClearFunc, you may have trouble determining _which_ clipboard
+ * was cleared, if you reuse your ClearFunc for multiple clipboards.
+ */
+typedef void (* GtkClipboardGetFunc)          (GtkClipboard     *clipboard,
+                                              GtkSelectionData *selection_data,
+                                              guint             info,
+                                              gpointer          user_data_or_owner);
+typedef void (* GtkClipboardClearFunc)        (GtkClipboard     *clipboard,
+                                              gpointer          user_data_or_owner);
+  
+GtkClipboard *gtk_clipboard_get (GdkAtom selection);
+
+gboolean gtk_clipboard_set_with_data  (GtkClipboard          *clipboard,
+                                      const GtkTargetEntry  *targets,
+                                      guint                  n_targets,
+                                      GtkClipboardGetFunc    get_func,
+                                      GtkClipboardClearFunc  clear_func,
+                                      gpointer               user_data);
+gboolean gtk_clipboard_set_with_owner (GtkClipboard          *clipboard,
+                                      const GtkTargetEntry  *targets,
+                                      guint                  n_targets,
+                                      GtkClipboardGetFunc    get_func,
+                                      GtkClipboardClearFunc  clear_func,
+                                      GObject               *owner);
+GObject *gtk_clipboard_get_owner      (GtkClipboard          *clipboard);
+void     gtk_clipboard_clear          (GtkClipboard          *clipboard);
+void     gtk_clipboard_set_text       (GtkClipboard          *clipboard,
+                                      const gchar           *text,
+                                      gint                   len);
+
+void gtk_clipboard_request_contents (GtkClipboard                 *clipboard,
+                                    GdkAtom                       target,
+                                    GtkClipboardReceivedFunc      callback,
+                                    gpointer                      user_data);
+void gtk_clipboard_request_text     (GtkClipboard                 *clipboard,
+                                    GtkClipboardTextReceivedFunc  callback,
+                                    gpointer                      user_data);
+
+GtkSelectionData *gtk_clipboard_wait_for_contents (GtkClipboard *clipboard,
+                                                  GdkAtom       target);
+gchar *           gtk_clipboard_wait_for_text     (GtkClipboard *clipboard);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif __GTK_CLIPBOARD_H__
index 1971b7e2ee68c5d1d4871287d573c094b2768359..dd8fe45fc08102a5b50d3aef76f2f2e5e55ceb74 100644 (file)
@@ -66,6 +66,19 @@ typedef enum
   GTK_CURVE_TYPE_FREE          /* free form curve */
 } GtkCurveType;
  
+typedef enum {
+  GTK_DELETE_CHARS,
+  GTK_DELETE_WORD_ENDS,           /* delete only the portion of the word to the
+                                   * left/right of cursor if we're in the middle
+                                   * of a word */
+  GTK_DELETE_WORDS,
+  GTK_DELETE_DISPLAY_LINES,
+  GTK_DELETE_DISPLAY_LINE_ENDS,
+  GTK_DELETE_PARAGRAPH_ENDS,      /* like C-k in Emacs (or its reverse) */
+  GTK_DELETE_PARAGRAPHS,          /* C-k in pico, kill whole line */
+  GTK_DELETE_WHITESPACE,          /* M-\ in Emacs */
+} GtkDeleteType;
+
 /* Focus movement types */
 typedef enum
 {
@@ -128,6 +141,18 @@ typedef enum
   GTK_CENTIMETERS
 } GtkMetricType;
 
+typedef enum {
+  GTK_MOVEMENT_CHARS,             /* move by forw/back chars */
+  GTK_MOVEMENT_POSITIONS,         /* move by left/right chars */
+  GTK_MOVEMENT_WORDS,             /* move by forward/back words */
+  GTK_MOVEMENT_DISPLAY_LINES,     /* move up/down lines (wrapped lines) */
+  GTK_MOVEMENT_DISPLAY_LINE_ENDS, /* move up/down lines (wrapped lines) */
+  GTK_MOVEMENT_PARAGRAPHS,        /* move up/down paragraphs (newline-ended lines) */
+  GTK_MOVEMENT_PARAGRAPH_ENDS,    /* move to either end of a paragraph */
+  GTK_MOVEMENT_PAGES,            /* move by pages */
+  GTK_MOVEMENT_BUFFER_ENDS        /* move to ends of the buffer */
+} GtkMovementStep;
+
 /* Orientation for toolbars, etc. */
 typedef enum
 {
index c18c789348a909ce32cdaa94690964335f1d8079..e7d8cc4088b0a6b9e0ea6511e6500905c5c2b871 100644 (file)
@@ -102,7 +102,10 @@ gtk_invisible_destroy (GtkObject *object)
 GtkWidget*
 gtk_invisible_new (void)
 {
-  return GTK_WIDGET (gtk_type_new (GTK_TYPE_INVISIBLE));
+  GtkWidget *result = GTK_WIDGET (gtk_type_new (GTK_TYPE_INVISIBLE));
+  gtk_widget_realize (result);
+
+  return result;
 }
 
 static void
index 121beb09854a9c0050df6873700ac16155ad2042..7a690d2acd26c63194987510ec9132fe87f14bc7 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <gmodule.h>
-#include "gtkbutton.h"
 #include "gtkdnd.h"
 #include "gtkcompat.h"
-#include "gtkhscrollbar.h"
-#include "gtkhseparator.h"
 #include "gtkmain.h"
-#include "gtkpreview.h"
 #include "gtkrc.h"
-#include "gtkscrolledwindow.h"
 #include "gtkselection.h"
 #include "gtksignal.h"
-#include "gtktable.h"
-#include "gtktext.h"
-#include "gtkvbox.h"
-#include "gtkvscrollbar.h"
 #include "gtkwidget.h"
 #include "gtkwindow.h"
 #include "gtkprivate.h"
@@ -1292,11 +1283,28 @@ GdkEvent*
 gtk_get_current_event (void)
 {
   if (current_events)
-    return gdk_event_copy ((GdkEvent *) current_events->data);
+    return gdk_event_copy (current_events->data);
   else
     return NULL;
 }
 
+/**
+ * gtk_get_current_event_time:
+ * 
+ * If there is a current event and it has a timestamp, return that
+ * timestamp, otherwise return %GDK_CURRENT_TIME.
+ * 
+ * Return value: the timestamp from the current event, or %GDK_CURRENT_TIME.
+ **/
+guint32
+gtk_get_current_event_time (void)
+{
+  if (current_events)
+    return gdk_event_get_time (current_events->data);
+  else
+    return GDK_CURRENT_TIME;
+}
+
 GtkWidget*
 gtk_get_event_widget (GdkEvent *event)
 {
@@ -1315,7 +1323,6 @@ gtk_exit_func (void)
   if (gtk_initialized)
     {
       gtk_initialized = FALSE;
-      gtk_preview_uninit ();
     }
 }
 
index ea9829b6b683f9db279ad45b156f411e30bf3672..73837885640b62f8442522992228a066e72cc145 100644 (file)
@@ -179,7 +179,9 @@ guint          gtk_key_snooper_install (GtkKeySnoopFunc snooper,
                                    gpointer        func_data);
 void      gtk_key_snooper_remove  (guint           snooper_handler_id);
   
-GdkEvent*  gtk_get_current_event   (void);
+GdkEvent*  gtk_get_current_event      (void);
+guint32    gtk_get_current_event_time (void);
+
 GtkWidget* gtk_get_event_widget           (GdkEvent       *event);
 
 
index f2e07c3e32bfa07a4ff94b4e3887c6ba6c589f1a..e09da3d65e604f4958837156e5d359c2878c77c5 100644 (file)
@@ -313,7 +313,7 @@ gtk_target_list_find (GtkTargetList *list,
  *   results:
  *************************************************************/
 
-gint
+gboolean
 gtk_selection_owner_set (GtkWidget *widget,
                         GdkAtom    selection,
                         guint32    time)
@@ -322,34 +322,26 @@ gtk_selection_owner_set (GtkWidget *widget,
   GtkWidget *old_owner;
   GtkSelectionInfo *selection_info = NULL;
   GdkWindow *window;
+
+  g_return_val_if_fail (widget == NULL || GTK_WIDGET_REALIZED (widget), FALSE);
   
   if (widget == NULL)
     window = NULL;
   else
-    {
-      if (!GTK_WIDGET_REALIZED (widget))
-       gtk_widget_realize (widget);
-      
-      window = widget->window;
-    }
-  
+    window = widget->window;
+
   tmp_list = current_selections;
   while (tmp_list)
     {
-      selection_info = (GtkSelectionInfo *)tmp_list->data;
-      
-      if (selection_info->selection == selection)
-       break;
+      if (((GtkSelectionInfo *)tmp_list->data)->selection == selection)
+       {
+         selection_info = tmp_list->data;
+         break;
+       }
       
       tmp_list = tmp_list->next;
     }
   
-  if (tmp_list == NULL)
-    selection_info = NULL;
-  else
-    if (selection_info->widget == widget)
-      return TRUE;
-  
   if (gdk_selection_owner_set (window, selection, time, TRUE))
     {
       old_owner = NULL;
@@ -373,8 +365,8 @@ gtk_selection_owner_set (GtkWidget *widget,
              selection_info->selection = selection;
              selection_info->widget = widget;
              selection_info->time = time;
-             current_selections = g_list_append (current_selections, 
-                                                 selection_info);
+             current_selections = g_list_prepend (current_selections, 
+                                                  selection_info);
            }
          else
            {
@@ -384,9 +376,9 @@ gtk_selection_owner_set (GtkWidget *widget,
            }
        }
       /* If another widget in the application lost the selection,
-       *  send it a GDK_SELECTION_CLEAR event, unless we're setting
-       *  the owner to None, in which case an event will be sent */
-      if (old_owner && (widget != NULL))
+       *  send it a GDK_SELECTION_CLEAR event.
+       */
+      if (old_owner && old_owner != widget)
        {
          GdkEventSelection event;
          
@@ -476,6 +468,43 @@ gtk_selection_target_list_remove (GtkWidget    *widget)
   gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, NULL);
 }
 
+/**
+ * gtk_selection_clear_targets:
+ * @widget:    a #GtkWidget
+ * @selection: an atom representing a selection
+ *
+ * Remove all targets registered for the given selection for the
+ * widget.
+ **/
+void 
+gtk_selection_clear_targets (GtkWidget *widget,
+                            GdkAtom    selection)
+{
+  GtkSelectionTargetList *sellist;
+  GList *tmp_list;
+  GList *lists;
+
+  lists = gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
+  
+  tmp_list = lists;
+  while (tmp_list)
+    {
+      sellist = tmp_list->data;
+      if (sellist->selection == selection)
+       {
+         lists = g_list_delete_link (lists, tmp_list);
+         gtk_target_list_unref (sellist->list);
+         g_free (sellist);
+
+         break;
+       }
+      
+      tmp_list = tmp_list->next;
+    }
+  
+  gtk_object_set_data (GTK_OBJECT (widget), gtk_selection_handler_key, lists);
+}
+
 void 
 gtk_selection_add_target (GtkWidget        *widget, 
                          GdkAtom            selection,
@@ -505,6 +534,7 @@ gtk_selection_add_targets (GtkWidget            *widget,
   gtk_target_list_add_table (list, targets, ntargets);
 }
 
+
 /*************************************************************
  * gtk_selection_remove_all:
  *     Removes all handlers and unsets ownership of all 
@@ -728,6 +758,122 @@ gtk_selection_data_set (GtkSelectionData *selection_data,
   selection_data->length = length;
 }
 
+static GdkAtom utf8_atom;
+static GdkAtom text_atom;
+static GdkAtom ctext_atom;
+
+static void 
+init_atoms (void)
+{
+  if (!utf8_atom)
+    {
+      utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
+      text_atom = gdk_atom_intern ("TEXT", FALSE);
+      ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
+    }
+}
+
+/**
+ * gtk_selection_data_set_text:
+ * @selection_data: a #GtkSelectionData
+ * @str: a UTF-8 string
+ * 
+ * Sets the contents of the selection from a UTF-8 encoded string.
+ * The string is converted to the form determined by
+ * @selection_data->target.
+ * 
+ * Return value: %TRUE if the selection was succesfully set,
+ *   otherwise %FALSE.
+ **/
+gboolean
+gtk_selection_data_set_text (GtkSelectionData     *selection_data,
+                            const guchar         *str)
+{
+  init_atoms ();
+
+  if (selection_data->target == utf8_atom)
+    {
+      gtk_selection_data_set (selection_data,
+                             utf8_atom,
+                             8, (guchar *)str, strlen (str));
+      return TRUE;
+    }
+  else if (selection_data->target == GDK_TARGET_STRING)
+    {
+      gchar *latin1 = gdk_utf8_to_string_target (str);
+
+      if (latin1)
+       {
+         gtk_selection_data_set (selection_data,
+                                 GDK_SELECTION_TYPE_STRING,
+                                 8, latin1, strlen (latin1));
+         g_free(latin1);
+         
+         return TRUE;
+       }
+
+    }
+  else if (selection_data->target == ctext_atom ||
+          selection_data->target == text_atom)
+    {
+      guchar *text;
+      GdkAtom encoding;
+      gint format;
+      gint new_length;
+      
+      if (gdk_utf8_to_compound_text (str, &encoding, &format, &text, &new_length))
+       {
+         gtk_selection_data_set (selection_data, encoding, format, text, new_length);
+         gdk_free_compound_text (text);
+      
+         return TRUE;
+       }
+    }
+  
+  return FALSE;
+}
+
+/**
+ * gtk_selection_data_get_text:
+ * @selection_data: a #GtkSelectionData
+ * 
+ * Gets the contents of the selection data as a UTF-8 string.
+ * 
+ * Return value: if the selection data contained a recognized
+ *   text type and it could be converted to UTF-8, a newly allocated
+ *   string containing the converted text, otherwise %NULL.
+ *   If the result is non-%NULL it must be freed with g_free().
+ **/
+guchar *
+gtk_selection_data_get_text (GtkSelectionData *selection_data)
+{
+  guchar *result = NULL;
+
+  init_atoms ();
+  
+  if (selection_data->length >= 0 &&
+      (selection_data->type == GDK_TARGET_STRING ||
+       selection_data->type == ctext_atom ||
+       selection_data->type == utf8_atom))
+    {
+      gchar **list;
+      gint i;
+      gint count = gdk_text_property_to_utf8_list (selection_data->type,
+                                                  selection_data->format, 
+                                                  selection_data->data,
+                                                  selection_data->length,
+                                                  &list);
+      if (count > 0)
+       result = list[0];
+
+      for (i = 1; i < count; i++)
+       g_free (list[i]);
+      g_free (list);
+    }
+
+  return result;
+}
+
 /*************************************************************
  * gtk_selection_init:
  *     Initialize local variables
@@ -755,16 +901,13 @@ gtk_selection_init (void)
  *************************************************************/
 
 gint
-gtk_selection_clear (GtkWidget *widget,
+gtk_selection_clear (GtkWidget         *widget,
                     GdkEventSelection *event)
 {
-  /* FIXME: there can be a problem if we change the selection
-     via gtk_selection_owner_set after another client claims 
-     the selection, but before we get the notification event.
-     Tk filters based on serial #'s, which aren't retained by
-     GTK. Filtering based on time's will be inherently 
-     somewhat unreliable. */
-  
+  /* Note that we filter clear events in gdkselection-x11.c, so
+   * that we only will get here if the clear event actually
+   * represents a change that we didn't do ourself.
+   */
   GList *tmp_list;
   GtkSelectionInfo *selection_info = NULL;
   
@@ -782,16 +925,9 @@ gtk_selection_clear (GtkWidget *widget,
   
   if (tmp_list)
     {
-      if (selection_info->time > event->time)
-       return FALSE;           /* return FALSE to indicate that
-                                * the selection was out of date,
-                                * and this clear should be ignored */
-      else
-       {
-         current_selections = g_list_remove_link (current_selections, tmp_list);
-         g_list_free (tmp_list);
-         g_free (selection_info);
-       }
+      current_selections = g_list_remove_link (current_selections, tmp_list);
+      g_list_free (tmp_list);
+      g_free (selection_info);
     }
   
   return TRUE;
@@ -1585,14 +1721,20 @@ gtk_selection_default_handler (GtkWidget        *widget,
 
 
 GtkSelectioData*
-gtk_selection_data_copy (GtkSelectionData *data)
+gtk_selection_data_copy (GtkSelectionData *selection_data)
 {
   GtkSelectionData *new_data;
   
-  g_return_val_if_fail (data != NULL, NULL);
+  g_return_val_if_fail (selection_data != NULL, NULL);
   
   new_data = g_new (GtkSelectionData, 1);
-  *new_data = *data;
+  *new_data = *selection_data;
+
+  if (selection_data->data)
+    {
+      new_data->data = g_malloc (selection_data->length + 1);
+      memcpy (new_data->data, selection_data->data, selection_data->length + 1);
+    }
   
   return new_data;
 }
@@ -1602,6 +1744,9 @@ gtk_selection_data_free (GtkSelectionData *data)
 {
   g_return_if_fail (data != NULL);
   
+  if (data->data)
+    g_free (data->data);
+  
   g_free (data);
 }
 
index 157896f1972e7692f554b5b0b9c21c98a4aff2c6..668fec4b3a3893711b3991d1633c3ee0543e7d90 100644 (file)
@@ -83,28 +83,31 @@ gboolean       gtk_target_list_find      (GtkTargetList  *list,
 
 /* Public interface */
 
-gint gtk_selection_owner_set (GtkWidget          *widget,
-                             GdkAtom              selection,
-                             guint32              time);
-void gtk_selection_add_target (GtkWidget           *widget, 
-                              GdkAtom              selection,
-                              GdkAtom              target,
-                              guint                info);
-void gtk_selection_add_targets (GtkWidget            *widget, 
-                               GdkAtom               selection,
-                               const GtkTargetEntry *targets,
-                               guint                 ntargets);
-gint gtk_selection_convert   (GtkWidget          *widget, 
-                             GdkAtom              selection, 
-                             GdkAtom              target,
-                             guint32              time);
-
-
-void gtk_selection_data_set (GtkSelectionData *selection_data,
-                            GdkAtom           type,
-                            gint              format,
-                            const guchar     *data,
-                            gint              length);
+gboolean gtk_selection_owner_set     (GtkWidget            *widget,
+                                     GdkAtom               selection,
+                                     guint32               time);
+void     gtk_selection_add_target    (GtkWidget            *widget,
+                                     GdkAtom               selection,
+                                     GdkAtom               target,
+                                     guint                 info);
+void     gtk_selection_add_targets   (GtkWidget            *widget,
+                                     GdkAtom               selection,
+                                     const GtkTargetEntry *targets,
+                                     guint                 ntargets);
+void     gtk_selection_clear_targets (GtkWidget            *widget,
+                                     GdkAtom               selection);
+gint     gtk_selection_convert       (GtkWidget            *widget,
+                                     GdkAtom               selection,
+                                     GdkAtom               target,
+                                     guint32               time);
+void     gtk_selection_data_set      (GtkSelectionData     *selection_data,
+                                     GdkAtom               type,
+                                     gint                  format,
+                                     const guchar         *data,
+                                     gint                  length);
+gboolean gtk_selection_data_set_text (GtkSelectionData     *selection_data,
+                                     const guchar         *str);
+guchar * gtk_selection_data_get_text (GtkSelectionData     *selection_data);
 
 /* Called when a widget is destroyed */
 
index 52f7e664d3f0355f9fa3acf5a99a53f0ccf9d40d..19549efdf96a9dda2afdb8b0bbe06105b139ad24 100644 (file)
@@ -5,14 +5,24 @@
 
 #include <string.h>
 
+#include "gtkclipboard.h"
 #include "gtkinvisible.h"
-#include "gtkselection.h"
 #include "gtksignal.h"
 #include "gtktextbuffer.h"
 #include "gtktextbtree.h"
 #include "gtktextiterprivate.h"
 #include <string.h>
 
+typedef struct _ClipboardRequest ClipboardRequest;
+
+struct _ClipboardRequest
+{
+  GtkTextBuffer *buffer;
+  gboolean interactive;
+  gboolean default_editable;
+  gboolean is_clipboard;
+};
+
 enum {
   INSERT_TEXT,
   DELETE_TEXT,
@@ -44,7 +54,6 @@ static void gtk_text_buffer_finalize   (GObject            *object);
 
 
 static void gtk_text_buffer_update_primary_selection   (GtkTextBuffer     *buffer);
-static void gtk_text_buffer_update_clipboard_selection (GtkTextBuffer     *buffer);
 static void gtk_text_buffer_real_insert_text           (GtkTextBuffer     *buffer,
                                                         GtkTextIter       *iter,
                                                         const gchar       *text,
@@ -70,11 +79,6 @@ void gtk_marshal_NONE__INT_POINTER_INT (GtkObject  *object,
                                         gpointer func_data,
                                         GtkArg  *args);
 
-static GdkAtom clipboard_atom = GDK_NONE;
-static GdkAtom text_atom = GDK_NONE;
-static GdkAtom ctext_atom = GDK_NONE;
-static GdkAtom utf8_atom = GDK_NONE;
-
 static GtkObjectClass *parent_class = NULL;
 static guint signals[LAST_SIGNAL] = { 0 };
 
@@ -238,34 +242,6 @@ gtk_marshal_NONE__INT_POINTER_INT (GtkObject  *object,
 void
 gtk_text_buffer_init (GtkTextBuffer *buffer)
 {
-  static const GtkTargetEntry targets[] = {
-    { "STRING", 0, TARGET_STRING },
-    { "TEXT",   0, TARGET_TEXT }, 
-    { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
-    { "UTF8_STRING", 0, TARGET_UTF8_STRING }
-  };
-  static const gint n_targets = sizeof(targets) / sizeof(targets[0]);
-
-  if (!clipboard_atom)
-    clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
-
-  if (!text_atom)
-    text_atom = gdk_atom_intern ("TEXT", FALSE);
-
-  if (!ctext_atom)
-    ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
-
-  if (!utf8_atom)
-    utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
-  
-  buffer->selection_widget = gtk_invisible_new();
-  
-  gtk_selection_add_targets (buffer->selection_widget,
-                             GDK_SELECTION_PRIMARY,
-                            targets, n_targets);
-  gtk_selection_add_targets (buffer->selection_widget,
-                             clipboard_atom,
-                            targets, n_targets);
 }
 
 /**
@@ -301,9 +277,6 @@ gtk_text_buffer_destroy (GtkObject *object)
 
   buffer = GTK_TEXT_BUFFER (object);
 
-  gtk_widget_destroy(buffer->selection_widget);
-  buffer->selection_widget = NULL;
-
   if (buffer->tag_table)
     {
       gtk_object_unref(GTK_OBJECT(buffer->tag_table));
@@ -1283,41 +1256,6 @@ gtk_text_buffer_set_modified (GtkTextBuffer      *buffer,
 }
 
 
-/*
- * Clipboard
- */
-
-static void
-set_clipboard_contents_nocopy(GtkTextBuffer *buffer,
-                              gchar *text)
-{
-  if (text && *text == '\0')
-    {
-      g_free(text);
-      text = NULL;
-    }
-  
-  if (buffer->clipboard_text != NULL)
-    g_free(buffer->clipboard_text);  
-
-  buffer->clipboard_text = text;
-  
-  gtk_text_buffer_update_clipboard_selection(buffer);
-}
-
-void
-gtk_text_buffer_set_clipboard_contents (GtkTextBuffer      *buffer,
-                                        const gchar         *text)
-{
-  set_clipboard_contents_nocopy(buffer, text ? g_strdup(text) : NULL);
-}
-
-const gchar*
-gtk_text_buffer_get_clipboard_contents (GtkTextBuffer      *buffer)
-{
-  return buffer->clipboard_text;
-}
-
 /*
  * Assorted other stuff
  */
@@ -1370,449 +1308,198 @@ gtk_text_buffer_get_tags               (GtkTextBuffer      *buffer,
   return retval;
 }
 
-/*
- * Selection
+/* Called when we lose the primary selection.
  */
-
-static gboolean
-have_primary_x_selection(GtkWidget *widget)
-{
-  return (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == 
-          widget->window);
-}
-
-static gboolean
-request_primary_x_selection(GtkWidget *widget, guint32 time)
-{
-  return gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, time);
-}
-
-static gboolean
-release_primary_x_selection(GtkWidget *widget, guint32 time)
-{
-  if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) == 
-      widget->window)
-    {
-      gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, time);
-      return TRUE;
-    }
-  else
-    return FALSE;
-}
-
-
-static gboolean
-have_clipboard_x_selection(GtkWidget *widget)
-{
-  return (gdk_selection_owner_get (clipboard_atom) == 
-          widget->window);
-}
-
-static gboolean
-request_clipboard_x_selection(GtkWidget *widget, guint32 time)
-{
-  return gtk_selection_owner_set (widget, clipboard_atom, time);
-}
-
-static gboolean
-release_clipboard_x_selection(GtkWidget *widget, guint32 time)
-{
-  if (gdk_selection_owner_get (clipboard_atom) == 
-      widget->window)
-    {
-      gtk_selection_owner_set (NULL, clipboard_atom, time);
-      return TRUE;
-    }
-  else
-    return FALSE;
-}
-
-/* Called when we lose the selection */
-static gint
-selection_clear_event(GtkWidget *widget, GdkEventSelection *event,
-                      gpointer data)
+static void
+clipboard_clear_cb (GtkClipboard *clipboard,
+                   gpointer      data)
 {
-  GtkTextBuffer *buffer;
+  /* Move selection_bound to the insertion point */
+  GtkTextIter insert;
+  GtkTextIter selection_bound;
+  GtkTextBuffer *buffer = GTK_TEXT_BUFFER (data);
   
-  buffer = GTK_TEXT_BUFFER(data);
-
-  /* Let the selection handling code know that the selection
-   * has been changed, since we've overriden the default handler */
-  if (!gtk_selection_clear (widget, event))
-    return FALSE;
-
-  buffer->have_selection = FALSE;
+  gtk_text_buffer_get_iter_at_mark(buffer, &insert,
+                                  gtk_text_buffer_get_mark (buffer, "insert"));
+  gtk_text_buffer_get_iter_at_mark(buffer, &selection_bound,
+                                  gtk_text_buffer_get_mark (buffer, "selection_bound"));
   
-  if (event->selection == GDK_SELECTION_PRIMARY)
-    {
-      /* Move selection_bound to the insertion point */
-      GtkTextIter insert;
-      GtkTextIter selection_bound;
-
-      gtk_text_buffer_get_iter_at_mark(buffer, &insert,
-                                       gtk_text_buffer_get_mark (buffer, "insert"));
-      gtk_text_buffer_get_iter_at_mark(buffer, &selection_bound,
-                                       gtk_text_buffer_get_mark (buffer, "selection_bound"));
-      
-      if (!gtk_text_iter_equal(&insert, &selection_bound))
-        gtk_text_buffer_move_mark(buffer,
-                                  gtk_text_buffer_get_mark (buffer, "selection_bound"),
-                                  &insert);
-    }
-  else if (event->selection == clipboard_atom)
-    {
-      gtk_text_buffer_set_clipboard_contents(buffer, NULL);
-    }
-
-  return TRUE;
+  if (!gtk_text_iter_equal(&insert, &selection_bound))
+    gtk_text_buffer_move_mark(buffer,
+                             gtk_text_buffer_get_mark (buffer, "selection_bound"),
+                             &insert);
 }
 
-/* Called when we have the selection and someone else wants our
-   data in order to paste it */
+/* Called when we have the primary selection and someone else wants our
+ * data in order to paste it.
+ */
 static void
-selection_get (GtkWidget         *widget,
-               GtkSelectionData  *selection_data,
-               guint              info,
-               guint              time,
-               gpointer data)
+clipboard_get_cb (GtkClipboard     *clipboard,
+                 GtkSelectionData *selection_data,
+                 guint             info,
+                 gpointer          data)
 {
-  GtkTextBuffer *buffer;
+  GtkTextBuffer *buffer = GTK_TEXT_BUFFER(data);
   gchar *str;
-  guint length;
-  
-  buffer = GTK_TEXT_BUFFER(data);
+  GtkTextIter start, end;
   
-  if (selection_data->selection == GDK_SELECTION_PRIMARY)
-    {
-      GtkTextIter start;
-      GtkTextIter end;
-
-      if (gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
-        {
-          /* Extract the selected text */
-          str = gtk_text_iter_get_visible_text(&start, &end);
-          
-          length = strlen(str);
-        }
-      else
-        return;
-    }
-  else
-    {
-      const gchar *cstr;
-      
-      cstr = gtk_text_buffer_get_clipboard_contents(buffer);
-
-      if (cstr == NULL)
-        return;
-
-      str = g_strdup(cstr);
-      
-      length = strlen (str);
-    }
-
-  if (str)
+  if (gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
     {
-      if (info == TARGET_UTF8_STRING)
-        {
-          /* Pass raw UTF8 */
-          gtk_selection_data_set (selection_data,
-                                  utf8_atom,
-                                  8*sizeof(gchar), (guchar *)str, length);
-
-        }
-      else if (info == TARGET_STRING ||
-               info == TARGET_TEXT)
-        {
-          gchar *latin1;
-
-          latin1 = gtk_text_utf_to_latin1(str, length);
-          
-          gtk_selection_data_set (selection_data,
-                                  GDK_SELECTION_TYPE_STRING,
-                                  8*sizeof(gchar), latin1, strlen(latin1));
-          g_free(latin1);
-        }
-      else if (info == TARGET_COMPOUND_TEXT)
-        {
-          /* FIXME convert UTF8 directly to current locale, not via
-             latin1 */
-          
-          guchar *text;
-          GdkAtom encoding;
-          gint format;
-          gint new_length;
-          gchar *latin1;
-
-          latin1 = gtk_text_utf_to_latin1(str, length);
-          
-          gdk_string_to_compound_text (latin1, &encoding, &format, &text, &new_length);
-          gtk_selection_data_set (selection_data, encoding, format, text, new_length);
-          gdk_free_compound_text (text);
-
-          g_free(latin1);
-        }
-
+      /* Extract the selected text */
+      str = gtk_text_iter_get_visible_text(&start, &end);
+      gtk_selection_data_set_text (selection_data, str);
       g_free (str);
     }
 }
 
-/* Called when we request a paste and receive the data */
+/* Called when we request a paste and receive the data
+ */
 static void
-selection_received (GtkWidget        *widget,
-                    GtkSelectionData *selection_data,
-                    guint             time,
-                    gpointer data)
+clipboard_received (GtkClipboard *clipboard,
+                   const gchar  *str,
+                   gpointer      data)
 {
-  GtkTextBuffer *buffer;
-  gboolean reselect;
-  GtkTextIter insert_point;
-  GtkTextMark *paste_point_override;
-  enum {INVALID, STRING, CTEXT, UTF8} type;
-
-  g_return_if_fail (widget != NULL);
-  
-  buffer = GTK_TEXT_BUFFER(data);
-
-  if (selection_data->type == GDK_TARGET_STRING)
-    type = STRING;
-  else if (selection_data->type == ctext_atom)
-    type = CTEXT;
-  else if (selection_data->type == utf8_atom)
-    type = UTF8;
-  else
-    type = INVALID;
-
-  if (type == INVALID || selection_data->length < 0)
-    {
-      /* If we asked for UTF8 and didn't get it, try text; if we asked
-         for text and didn't get it, try string.  If we asked for
-         anything else and didn't get it, give up. */
-      if (selection_data->target == utf8_atom)
-        gtk_selection_convert (widget, selection_data->selection,
-                               GDK_TARGET_STRING, time);
-      return;
-    }
-
-  paste_point_override = gtk_text_buffer_get_mark (buffer,
-                                                   "__paste_point_override");
-  
-  if (paste_point_override != NULL)
-    {
-      gtk_text_buffer_get_iter_at_mark(buffer, &insert_point,
-                                       paste_point_override);
-      gtk_text_buffer_delete_mark(buffer,
-                                  gtk_text_buffer_get_mark (buffer,
-                                                            "__paste_point_override"));
-    }
-  else
-    {
-      gtk_text_buffer_get_iter_at_mark(buffer, &insert_point,
-                                       gtk_text_buffer_get_mark (buffer,
-                                                                 "insert"));
-    }
-  
-  reselect = FALSE;
+  ClipboardRequest *request_data = data;
+  GtkTextBuffer *buffer = request_data->buffer;
 
-  if ((TRUE/* Text is selected FIXME */) && 
-      (!buffer->have_selection ||
-       (selection_data->selection == clipboard_atom)))
+  if (str)
     {
-      reselect = TRUE;
-
-      if (buffer->have_selection)
-        {
-          /* FIXME Delete currently-selected chars but don't give up X
-             selection since we'll use the newly-pasted stuff as
-             a new X selection */
+      gboolean reselect;
+      GtkTextIter insert_point;
+      GtkTextMark *paste_point_override;
 
-        }
+      paste_point_override = gtk_text_buffer_get_mark (buffer,
+                                                      "__paste_point_override");
+      
+      if (paste_point_override != NULL)
+       {
+         gtk_text_buffer_get_iter_at_mark(buffer, &insert_point,
+                                          paste_point_override);
+         gtk_text_buffer_delete_mark(buffer,
+                                     gtk_text_buffer_get_mark (buffer,
+                                                               "__paste_point_override"));
+       }
       else
-        ; /* FIXME Delete selected chars and give up X selection */
-    }
-
-  switch (type)
-    {
-    case STRING:
-      {
-        gchar *utf;
-        
-        utf = gtk_text_latin1_to_utf((const gchar*)selection_data->data,
-                                     selection_data->length);
-        if (buffer->paste_interactive)
-          gtk_text_buffer_insert_interactive (buffer, &insert_point,
-                                              utf, -1, buffer->paste_default_editable);
-        else
-          gtk_text_buffer_insert (buffer, &insert_point,
-                                  utf, -1);
-        g_free(utf);
-      }
-      break;
+       {
+         gtk_text_buffer_get_iter_at_mark(buffer, &insert_point,
+                                          gtk_text_buffer_get_mark (buffer,
+                                                                    "insert"));
+       }
       
-    case UTF8:
-      gtk_text_buffer_insert (buffer, &insert_point,
-                              (const gchar *)selection_data->data,
-                              selection_data->length);
-      break;
+      reselect = FALSE;
+
+      /* FIXME ALL OF THIS - I think that the "best method" is that when pasting
+       * with the cursor inside the selection area, you replace the selection
+       * with the new text, otherwise, you simply insert the new text at
+       * the point where the click occured, unselecting any selected text.
+       *
+       * This probably is best implemented as a "replace_selection" flag in
+       * ClipboardRequest.
+       */
+#if 0      
+      if ((TRUE/* Text is selected FIXME */) && 
+         (!buffer->have_selection || request_data->is_clipboard))
+       {
+         reselect = TRUE;
+         
+         if (buffer->have_selection)
+           {
+             /* FIXME Delete currently-selected chars but don't give up X
+                selection since we'll use the newly-pasted stuff as
+                a new X selection */
+             
+           }
+         else
+           ; /* FIXME Delete selected chars and give up X selection */
+       }
+#endif
       
-    case CTEXT:
-      {
-       gchar **list;
-       gint count;
-       gint i;
-
-       count = gdk_text_property_to_text_list (selection_data->type,
-                                               selection_data->format, 
-                                               selection_data->data,
-                                               selection_data->length,
-                                               &list);
-       for (i=0; i<count; i++)
-          {
-            /* list contains stuff in our default encoding */
-            gboolean free_utf = FALSE;
-            gchar *utf = NULL;
-            gchar *charset = NULL;
-            
-            if (g_get_charset (&charset))
-              {
-                utf = g_convert (list[i], -1,
-                                 "UTF8", charset,
-                                 NULL);
-                free_utf = TRUE;
-              }
-            else
-              utf = list[i];
-
-            if (buffer->paste_interactive)
-              gtk_text_buffer_insert_interactive (buffer, &insert_point,
-                                                  utf, -1, buffer->paste_default_editable);
-            else
-              gtk_text_buffer_insert (buffer, &insert_point,
-                                      utf, -1);
-
-            if (free_utf)
-              g_free(utf);
-          }
-
-       if (count > 0)
-         gdk_free_text_list (list);
-      }
-      break;
       
-    case INVALID:              /* quiet compiler */
-      break;
+      if (request_data->interactive)
+       gtk_text_buffer_insert_interactive (buffer, &insert_point,
+                                           str, -1, request_data->default_editable);
+      else
+       gtk_text_buffer_insert (buffer, &insert_point,
+                               str, -1);
+      
+      if (reselect)
+       ; /* FIXME Select the region of text we just inserted */
     }
 
-  if (reselect)
-    ; /* FIXME Select the region of text we just inserted */
-
+  g_object_unref (G_OBJECT (buffer));
+  g_free (request_data);
 }
 
 static void
-ensure_handlers(GtkTextBuffer *buffer)
+gtk_text_buffer_update_primary_selection (GtkTextBuffer *buffer)
 {
-  if (!buffer->selection_handlers_installed)
-    {
-      buffer->selection_handlers_installed = TRUE;
-
-      gtk_signal_connect(GTK_OBJECT(buffer->selection_widget),
-                         "selection_clear_event",
-                         GTK_SIGNAL_FUNC(selection_clear_event),
-                         buffer);
-
-      gtk_signal_connect(GTK_OBJECT(buffer->selection_widget),
-                         "selection_received",
-                         GTK_SIGNAL_FUNC(selection_received),
-                         buffer);
-
-      gtk_signal_connect(GTK_OBJECT(buffer->selection_widget),
-                         "selection_get",
-                         GTK_SIGNAL_FUNC(selection_get),
-                         buffer);
-    }
-}
+  static const GtkTargetEntry targets[] = {
+    { "STRING", 0, TARGET_STRING },
+    { "TEXT",   0, TARGET_TEXT }, 
+    { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
+    { "UTF8_STRING", 0, TARGET_UTF8_STRING }
+  };
 
-/* FIXME GDK_CURRENT_TIME should probably go away and we should
-   figure out how to get the events in here */
-static void
-gtk_text_buffer_update_primary_selection(GtkTextBuffer *buffer)
-{
   GtkTextIter start;
   GtkTextIter end;
 
-  ensure_handlers(buffer);
-  
+  GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_PRIMARY);
+
   /* Determine whether we have a selection and adjust X selection
-     accordingly. */
+   * accordingly.
+   */
   
-  if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
-    {
-      release_primary_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
-      buffer->have_selection = FALSE;
-    }
-  else
-    {
-      /* Even if we already have the selection, we need to update our
-         timestamp. */
-      buffer->have_selection = FALSE;
-      request_primary_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
-      if (have_primary_x_selection(buffer->selection_widget))
-        buffer->have_selection = TRUE;
-    }
-}
-
-static void
-gtk_text_buffer_update_clipboard_selection(GtkTextBuffer *buffer)
-{ 
-  if (buffer->clipboard_text == NULL ||
-      buffer->clipboard_text[0] == '\0')
+  if (!gtk_text_buffer_get_selection_bounds (buffer, &start, &end))
     {
-      release_clipboard_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
+      if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (buffer))
+       gtk_clipboard_clear (clipboard);
     }
   else
     {
       /* Even if we already have the selection, we need to update our
-         timestamp. */
-      request_clipboard_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
+       * timestamp.
+       */
+      if (!gtk_clipboard_set_with_owner (clipboard, targets, G_N_ELEMENTS (targets),
+                                        clipboard_get_cb, clipboard_clear_cb, G_OBJECT (buffer)))
+       clipboard_clear_cb (clipboard, buffer);
     }
 }
 
 static void
-paste (GtkTextBuffer *buffer, GdkAtom selection, guint32 time,
+paste (GtkTextBuffer *buffer, 
+       gboolean is_clipboard,
        gboolean interactive,
        gboolean default_editable)
 {
-  ensure_handlers(buffer);
+  ClipboardRequest *data = g_new (ClipboardRequest, 1);
 
-  buffer->paste_interactive = interactive;
-  buffer->paste_default_editable = default_editable;
-  
-  gtk_selection_convert (buffer->selection_widget, selection,
-                         utf8_atom, time);
+  data->buffer = buffer;
+  g_object_ref (G_OBJECT (buffer));
+  data->interactive = interactive;
+  data->default_editable = default_editable;
+
+  gtk_clipboard_request_text (gtk_clipboard_get (is_clipboard ? GDK_NONE : GDK_SELECTION_PRIMARY),
+                             clipboard_received, data);
 }
 
 void
-gtk_text_buffer_paste_primary_selection(GtkTextBuffer      *buffer,
-                                        GtkTextIter        *override_location,
-                                        guint32 time,
-                                        gboolean interactive,
-                                        gboolean default_editable)
+gtk_text_buffer_paste_primary (GtkTextBuffer *buffer,
+                              GtkTextIter   *override_location,
+                              gboolean       default_editable)
 {
   if (override_location != NULL)
     gtk_text_buffer_create_mark(buffer,
                                 "__paste_point_override",
                                 override_location, FALSE);
   
-  paste(buffer, GDK_SELECTION_PRIMARY, time, interactive, default_editable);
+  paste (buffer, FALSE, TRUE, default_editable);
 }
 
 void
-gtk_text_buffer_paste_clipboard        (GtkTextBuffer      *buffer,
-                                        guint32              time,
-                                        gboolean            interactive,
-                                        gboolean            default_editable)
+gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer,
+                                gboolean       default_editable)
 {
-  paste(buffer, clipboard_atom, time, interactive, default_editable);
+  paste (buffer, TRUE, TRUE, default_editable);
 }
 
 gboolean
@@ -1833,15 +1520,13 @@ gtk_text_buffer_delete_selection (GtkTextBuffer *buffer,
         gtk_text_buffer_delete_interactive (buffer, &start, &end, default_editable);
       else
         gtk_text_buffer_delete (buffer, &start, &end);
-      
-      gtk_text_buffer_update_primary_selection(buffer);
+
       return TRUE; /* We deleted stuff */
     }
 }
 
 static void
 cut_or_copy(GtkTextBuffer *buffer,
-            guint32 time,
             gboolean delete_region_after,
             gboolean interactive,
             gboolean default_editable)
@@ -1871,11 +1556,12 @@ cut_or_copy(GtkTextBuffer *buffer,
 
   if (!gtk_text_iter_equal(&start, &end))
     {
+      GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
       gchar *text;
       
-      text = gtk_text_iter_get_visible_text(&start, &end);
-      
-      set_clipboard_contents_nocopy(buffer, text);
+      text = gtk_text_iter_get_visible_text (&start, &end);
+      gtk_clipboard_set_text (clipboard, text, -1);
+      g_free (text);
       
       if (delete_region_after)
         {
@@ -1888,19 +1574,16 @@ cut_or_copy(GtkTextBuffer *buffer,
 }
 
 void
-gtk_text_buffer_cut (GtkTextBuffer      *buffer,
-                     guint32              time,
-                     gboolean interactive,
-                     gboolean default_editable)
+gtk_text_buffer_cut_clipboard (GtkTextBuffer *buffer,
+                              gboolean       default_editable)
 {
-  cut_or_copy(buffer, time, TRUE, interactive, default_editable);
+  cut_or_copy (buffer, TRUE, TRUE, default_editable);
 }
 
 void
-gtk_text_buffer_copy                   (GtkTextBuffer      *buffer,
-                                        guint32              time)
+gtk_text_buffer_copy_clipboard (GtkTextBuffer *buffer)
 {
-  cut_or_copy(buffer, time, FALSE, FALSE, TRUE);
+  cut_or_copy (buffer, FALSE, TRUE, TRUE);
 }
 
 
index 7680294b71bdc8d0665b75264c01e802258a18fe..b58feb62c42cf17026d7c00306e9f877de920310 100644 (file)
@@ -32,18 +32,8 @@ struct _GtkTextBuffer {
   GtkTextTagTable *tag_table;
   GtkTextBTree *btree;
 
-  /* Text currently pasted to the clipboard */
-  gchar *clipboard_text;
-
   /* Whether the buffer has been modified since last save */
   gboolean modified;
-
-  /* We use this for selections */
-  GtkWidget *selection_widget;
-  gboolean have_selection;
-  gboolean selection_handlers_installed;
-  gboolean paste_interactive;
-  gboolean paste_default_editable;
 };
 
 struct _GtkTextBufferClass {
@@ -233,33 +223,22 @@ GSList         *gtk_text_buffer_get_tags (GtkTextBuffer     *buffer,
 gboolean        gtk_text_buffer_modified                (GtkTextBuffer *buffer);
 void            gtk_text_buffer_set_modified            (GtkTextBuffer *buffer,
                                                          gboolean       setting);
-void            gtk_text_buffer_set_clipboard_contents  (GtkTextBuffer *buffer,
-                                                         const gchar   *text);
-const gchar    *gtk_text_buffer_get_clipboard_contents  (GtkTextBuffer *buffer);
 
-
-void            gtk_text_buffer_paste_primary_selection (GtkTextBuffer *buffer,
+void            gtk_text_buffer_paste_primary           (GtkTextBuffer *buffer,
                                                          GtkTextIter   *override_location,
-                                                         guint32        time,
-                                                         gboolean       interactive,
-                                                         gboolean       default_editable);
-gboolean        gtk_text_buffer_delete_selection        (GtkTextBuffer *buffer,
-                                                         gboolean       interactive,
                                                          gboolean       default_editable);
-void            gtk_text_buffer_cut                     (GtkTextBuffer *buffer,
-                                                         guint32        time,
-                                                         gboolean       interactive,
+void            gtk_text_buffer_cut_clipboard           (GtkTextBuffer *buffer,
                                                          gboolean       default_editable);
-void            gtk_text_buffer_copy                    (GtkTextBuffer *buffer,
-                                                         guint32        time);
+void            gtk_text_buffer_copy_clipboard          (GtkTextBuffer *buffer);
 void            gtk_text_buffer_paste_clipboard         (GtkTextBuffer *buffer,
-                                                         guint32        time,
-                                                         gboolean       interactive,
                                                          gboolean       default_editable);
+
 gboolean        gtk_text_buffer_get_selection_bounds    (GtkTextBuffer *buffer,
                                                          GtkTextIter   *start,
                                                          GtkTextIter   *end);
-
+gboolean        gtk_text_buffer_delete_selection        (GtkTextBuffer *buffer,
+                                                         gboolean       interactive,
+                                                         gboolean       default_editable);
 
 /* This function is not implemented. */
 gboolean gtk_text_buffer_find_string(GtkTextBuffer *buffer,
index af2b76edb2f2212c01fa79fac2c84353184571a4..901aa92666b2141e82ea6dfd4729178f0da784ae 100644 (file)
@@ -2061,6 +2061,60 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
     }
 }
 
+/**
+ * gtk_text_layout_move_iter_to_line_end:
+ * @layout: a #GtkTextLayout
+ * @direction: if negative, move to beginning of line, otherwise
+               move to end of line.
+ * 
+ * Move to the beginning or end of a display line.
+ **/
+void
+gtk_text_layout_move_iter_to_line_end (GtkTextLayout *layout,
+                                      GtkTextIter   *iter,
+                                      gint           direction)
+{
+  GtkTextLine *line;
+  GtkTextLineDisplay *display;
+  gint line_byte;
+  gint byte_offset = 0;
+  GSList *tmp_list;
+  
+  g_return_if_fail (layout != NULL);
+  g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+  g_return_if_fail (iter != NULL);
+  
+  line = gtk_text_iter_get_text_line (iter);
+  line_byte = gtk_text_iter_get_line_index (iter);
+  
+  display = gtk_text_layout_get_line_display (layout, line, FALSE);
+
+  tmp_list = pango_layout_get_lines (display->layout);
+  while (tmp_list)
+    {
+      PangoLayoutLine *layout_line = tmp_list->data;
+
+      if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
+       {
+         gtk_text_btree_get_iter_at_line (_gtk_text_buffer_get_btree (layout->buffer),
+                                          iter, line,
+                                          direction < 0 ? byte_offset : layout_line->length);
+
+         /* FIXME: Move back one position to avoid going to next line
+          */
+         if (direction < 0 && layout_line->length > 0)
+           gtk_text_iter_prev_char (iter);
+
+         break;
+       }
+      
+      byte_offset += layout_line->length;
+      tmp_list = tmp_list->next;
+    }
+  
+  gtk_text_layout_free_line_display (layout, display);
+}
+
 /**
  * gtk_text_layout_move_iter_to_x:
  * @layout: a #GtkTextLayout
index 048b55d7716977d79bda2fb3cdfa53349b71b361..e06ed4fd3faa7be1d5f3d0b8bd7b1778a20660a2 100644 (file)
@@ -245,6 +245,9 @@ gboolean gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout     *layout,
                                               gint               top,
                                               gint               bottom);
 
+void gtk_text_layout_move_iter_to_line_end      (GtkTextLayout *layout,
+                                                GtkTextIter   *iter,
+                                                gint           direction);
 void gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
                                                 GtkTextIter   *iter);
 void gtk_text_layout_move_iter_to_next_line     (GtkTextLayout *layout,
@@ -256,7 +259,6 @@ void gtk_text_layout_move_iter_visually         (GtkTextLayout *layout,
                                                 GtkTextIter   *iter,
                                                 gint           count);
 
-
 void gtk_text_layout_spew (GtkTextLayout *layout);
 
 #ifdef __cplusplus
index e97df15f7211f820c8c4087129f0b88f872e6ea5..55b378c6e92c5b2b710ee8effc038b8c578ccdda 100644 (file)
 #include <string.h>
 
 enum {
-  MOVE_INSERT,
+  MOVE,
   SET_ANCHOR,
-  SCROLL_TEXT,
-  DELETE_TEXT,
-  CUT_TEXT,
-  COPY_TEXT,
-  PASTE_TEXT,
+  INSERT,
+  DELETE,
+  CUT_CLIPBOARD,
+  COPY_CLIPBOARD,
+  PASTE_CLIPBOARD,
   TOGGLE_OVERWRITE,
   SET_SCROLL_ADJUSTMENTS,
   LAST_SIGNAL
@@ -168,21 +168,22 @@ static void gtk_text_view_set_scroll_adjustments (GtkTextView   *text_view,
                                                  GtkAdjustment *hadj,
                                                  GtkAdjustment *vadj);
 
-static void gtk_text_view_move_insert      (GtkTextView             *text_view,
-                                           GtkTextViewMovementStep  step,
-                                           gint                     count,
-                                           gboolean                 extend_selection);
-static void gtk_text_view_set_anchor       (GtkTextView             *text_view);
-static void gtk_text_view_scroll_text      (GtkTextView             *text_view,
-                                           GtkTextViewScrollType    type);
-static void gtk_text_view_delete_text      (GtkTextView             *text_view,
-                                           GtkTextViewDeleteType    type,
-                                           gint                     count);
-static void gtk_text_view_cut_text         (GtkTextView             *text_view);
-static void gtk_text_view_copy_text        (GtkTextView             *text_view);
-static void gtk_text_view_paste_text       (GtkTextView             *text_view);
-static void gtk_text_view_toggle_overwrite (GtkTextView             *text_view);
-
+static void gtk_text_view_move             (GtkTextView           *text_view,
+                                           GtkMovementStep        step,
+                                           gint                   count,
+                                           gboolean               extend_selection);
+static void gtk_text_view_set_anchor       (GtkTextView           *text_view);
+static void gtk_text_view_scroll_pages     (GtkTextView           *text_view,
+                                           gint                   count);
+static void gtk_text_view_insert           (GtkTextView           *text_view,
+                                           const gchar           *str);
+static void gtk_text_view_delete           (GtkTextView           *text_view,
+                                           GtkDeleteType          type,
+                                           gint                   count);
+static void gtk_text_view_cut_clipboard    (GtkTextView           *text_view);
+static void gtk_text_view_copy_clipboard   (GtkTextView           *text_view);
+static void gtk_text_view_paste_clipboard  (GtkTextView           *text_view);
+static void gtk_text_view_toggle_overwrite (GtkTextView           *text_view);
 
 static void     gtk_text_view_validate_onscreen     (GtkTextView        *text_view);
 static void     gtk_text_view_get_first_para_iter   (GtkTextView        *text_view,
@@ -231,12 +232,6 @@ enum {
   TARGET_UTF8_STRING
 };
 
-static GdkAtom clipboard_atom = GDK_NONE;
-static GdkAtom text_atom = GDK_NONE;
-static GdkAtom ctext_atom = GDK_NONE;
-static GdkAtom utf8_atom = GDK_NONE;
-
-
 static GtkTargetEntry target_table[] = {
   { "UTF8_STRING", 0, TARGET_UTF8_STRING },
   { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
@@ -245,8 +240,6 @@ static GtkTargetEntry target_table[] = {
   { "STRING",     0, TARGET_STRING }
 };
 
-static guint n_targets = sizeof (target_table) / sizeof (target_table[0]);
-
 static GtkContainerClass *parent_class = NULL;
 static guint signals[LAST_SIGNAL] = { 0 };
 
@@ -276,23 +269,23 @@ gtk_text_view_get_type (void)
 }
 
 static void
-add_move_insert_binding (GtkBindingSet *binding_set,
-                        guint keyval,
-                        guint modmask,
-                        GtkTextViewMovementStep step,
-                        gint count)
+add_move_binding (GtkBindingSet  *binding_set,
+                 guint           keyval,
+                 guint           modmask,
+                 GtkMovementStep step,
+                 gint            count)
 {
   g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
   
   gtk_binding_entry_add_signal (binding_set, keyval, modmask,
-                               "move_insert", 3,
+                               "move", 3,
                                GTK_TYPE_ENUM, step,
                                GTK_TYPE_INT, count,
                                 GTK_TYPE_BOOL, FALSE);
 
   /* Selection-extending version */
   gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
-                               "move_insert", 3,
+                               "move", 3,
                                GTK_TYPE_ENUM, step,
                                GTK_TYPE_INT, count,
                                 GTK_TYPE_BOOL, TRUE);
@@ -331,13 +324,13 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
    * Signals
    */
 
-  signals[MOVE_INSERT] = 
-      gtk_signal_new ("move_insert",
+  signals[MOVE] = 
+      gtk_signal_new ("move",
                       GTK_RUN_LAST | GTK_RUN_ACTION,
                       GTK_CLASS_TYPE (object_class),
-                      GTK_SIGNAL_OFFSET (GtkTextViewClass, move_insert),
+                      GTK_SIGNAL_OFFSET (GtkTextViewClass, move),
                       gtk_marshal_NONE__INT_INT_INT,
-                      GTK_TYPE_NONE, 3, GTK_TYPE_ENUM, GTK_TYPE_INT, GTK_TYPE_BOOL);
+                      GTK_TYPE_NONE, 3, GTK_TYPE_MOVEMENT_STEP, GTK_TYPE_INT, GTK_TYPE_BOOL);
 
   signals[SET_ANCHOR] = 
       gtk_signal_new ("set_anchor",
@@ -347,43 +340,43 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
                       gtk_marshal_NONE__NONE,
                       GTK_TYPE_NONE, 0);
 
-  signals[SCROLL_TEXT] = 
-      gtk_signal_new ("scroll_text",
+  signals[INSERT] = 
+      gtk_signal_new ("insert",
                       GTK_RUN_LAST | GTK_RUN_ACTION,
                       GTK_CLASS_TYPE (object_class),
-                      GTK_SIGNAL_OFFSET (GtkTextViewClass, scroll_text),
-                      gtk_marshal_NONE__INT,
-                      GTK_TYPE_NONE, 1, GTK_TYPE_ENUM);
+                      GTK_SIGNAL_OFFSET (GtkTextViewClass, insert),
+                      gtk_marshal_NONE__STRING,
+                      GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
 
-  signals[DELETE_TEXT] = 
-      gtk_signal_new ("delete_text",
+  signals[DELETE] = 
+      gtk_signal_new ("delete",
                       GTK_RUN_LAST | GTK_RUN_ACTION,
                       GTK_CLASS_TYPE (object_class),
-                      GTK_SIGNAL_OFFSET (GtkTextViewClass, delete_text),
+                      GTK_SIGNAL_OFFSET (GtkTextViewClass, delete),
                       gtk_marshal_NONE__INT_INT,
-                      GTK_TYPE_NONE, 2, GTK_TYPE_ENUM, GTK_TYPE_INT);
+                      GTK_TYPE_NONE, 2, GTK_TYPE_DELETE_TYPE, GTK_TYPE_INT);
 
-  signals[CUT_TEXT] =
-    gtk_signal_new ("cut_text",
+  signals[CUT_CLIPBOARD] =
+    gtk_signal_new ("cut_clipboard",
                     GTK_RUN_LAST | GTK_RUN_ACTION,
                     GTK_CLASS_TYPE (object_class),
-                    GTK_SIGNAL_OFFSET (GtkTextViewClass, cut_text),
+                    GTK_SIGNAL_OFFSET (GtkTextViewClass, cut_clipboard),
                     gtk_marshal_NONE__NONE,
                     GTK_TYPE_NONE, 0);
 
-  signals[COPY_TEXT] =
-    gtk_signal_new ("copy_text",
+  signals[COPY_CLIPBOARD] =
+    gtk_signal_new ("copy_clipboard",
                     GTK_RUN_LAST | GTK_RUN_ACTION,
                     GTK_CLASS_TYPE (object_class),
-                    GTK_SIGNAL_OFFSET (GtkTextViewClass, copy_text),
+                    GTK_SIGNAL_OFFSET (GtkTextViewClass, copy_clipboard),
                     gtk_marshal_NONE__NONE,
                     GTK_TYPE_NONE, 0);
 
-  signals[PASTE_TEXT] =
-    gtk_signal_new ("paste_text",
+  signals[PASTE_CLIPBOARD] =
+    gtk_signal_new ("paste_clipboard",
                     GTK_RUN_LAST | GTK_RUN_ACTION,
                     GTK_CLASS_TYPE (object_class),
-                    GTK_SIGNAL_OFFSET (GtkTextViewClass, paste_text),
+                    GTK_SIGNAL_OFFSET (GtkTextViewClass, paste_clipboard),
                     gtk_marshal_NONE__NONE,
                     GTK_TYPE_NONE, 0);
 
@@ -412,123 +405,142 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   binding_set = gtk_binding_set_by_class (klass);
 
   /* Moving the insertion point */
-  add_move_insert_binding (binding_set, GDK_Right, 0,
-                          GTK_TEXT_MOVEMENT_POSITIONS, 1);
+  add_move_binding (binding_set, GDK_Right, 0,
+                   GTK_MOVEMENT_POSITIONS, 1);
   
-  add_move_insert_binding (binding_set, GDK_Left, 0,
-                          GTK_TEXT_MOVEMENT_POSITIONS, -1);
+  add_move_binding (binding_set, GDK_Left, 0,
+                   GTK_MOVEMENT_POSITIONS, -1);
 
-  add_move_insert_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
-                          GTK_TEXT_MOVEMENT_CHAR, 1);
+  add_move_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
+                   GTK_MOVEMENT_CHARS, 1);
   
-  add_move_insert_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
-                          GTK_TEXT_MOVEMENT_CHAR, -1);
+  add_move_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
+                   GTK_MOVEMENT_CHARS, -1);
   
-  add_move_insert_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
-                          GTK_TEXT_MOVEMENT_WORD, 1);
+  add_move_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
+                   GTK_MOVEMENT_WORDS, 1);
 
-  add_move_insert_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
-                          GTK_TEXT_MOVEMENT_WORD, -1);
+  add_move_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
+                   GTK_MOVEMENT_WORDS, -1);
   
   /* Eventually we want to move by display lines, not paragraphs */
-  add_move_insert_binding (binding_set, GDK_Up, 0,
-                          GTK_TEXT_MOVEMENT_WRAPPED_LINE, -1);
+  add_move_binding (binding_set, GDK_Up, 0,
+                   GTK_MOVEMENT_DISPLAY_LINES, -1);
   
-  add_move_insert_binding (binding_set, GDK_Down, 0,
-                          GTK_TEXT_MOVEMENT_WRAPPED_LINE, 1);
+  add_move_binding (binding_set, GDK_Down, 0,
+                   GTK_MOVEMENT_DISPLAY_LINES, 1);
 
-  add_move_insert_binding (binding_set, GDK_p, GDK_CONTROL_MASK,
-                          GTK_TEXT_MOVEMENT_WRAPPED_LINE, -1);
+  add_move_binding (binding_set, GDK_p, GDK_CONTROL_MASK,
+                   GTK_MOVEMENT_DISPLAY_LINES, -1);
   
-  add_move_insert_binding (binding_set, GDK_n, GDK_CONTROL_MASK,
-                          GTK_TEXT_MOVEMENT_WRAPPED_LINE, 1);
+  add_move_binding (binding_set, GDK_n, GDK_CONTROL_MASK,
+                   GTK_MOVEMENT_DISPLAY_LINES, 1);
   
-  add_move_insert_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
-                          GTK_TEXT_MOVEMENT_LINE_ENDS, -1);
+  add_move_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
+                   GTK_MOVEMENT_PARAGRAPH_ENDS, -1);
 
-  add_move_insert_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
-                          GTK_TEXT_MOVEMENT_LINE_ENDS, 1);
+  add_move_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
+                   GTK_MOVEMENT_PARAGRAPH_ENDS, 1);
 
-  add_move_insert_binding (binding_set, GDK_f, GDK_MOD1_MASK,
-                          GTK_TEXT_MOVEMENT_WORD, 1);
+  add_move_binding (binding_set, GDK_f, GDK_MOD1_MASK,
+                   GTK_MOVEMENT_WORDS, 1);
 
-  add_move_insert_binding (binding_set, GDK_b, GDK_MOD1_MASK,
-                          GTK_TEXT_MOVEMENT_WORD, -1);
+  add_move_binding (binding_set, GDK_b, GDK_MOD1_MASK,
+                   GTK_MOVEMENT_WORDS, -1);
 
-  add_move_insert_binding (binding_set, GDK_Home, 0,
-                          GTK_TEXT_MOVEMENT_BUFFER_ENDS, -1);
+  add_move_binding (binding_set, GDK_Home, 0,
+                   GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
 
-  add_move_insert_binding (binding_set, GDK_End, 0,
-                          GTK_TEXT_MOVEMENT_BUFFER_ENDS, 1);
+  add_move_binding (binding_set, GDK_End, 0,
+                   GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
+  
+  add_move_binding (binding_set, GDK_Home, GDK_CONTROL_MASK,
+                   GTK_MOVEMENT_BUFFER_ENDS, -1);
+
+  add_move_binding (binding_set, GDK_End, GDK_CONTROL_MASK,
+                   GTK_MOVEMENT_BUFFER_ENDS, 1);
+
+  add_move_binding (binding_set, GDK_Page_Up, 0,
+                   GTK_MOVEMENT_PAGES, -1);
+
+  add_move_binding (binding_set, GDK_Page_Down, 0,
+                   GTK_MOVEMENT_PAGES, 1);
+  
   
   /* Setting the cut/paste/copy anchor */
   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK,
                                "set_anchor", 0);
 
-  
-  /* Scrolling around */
-  gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
-                               "scroll_text", 1,
-                               GTK_TYPE_ENUM, GTK_TEXT_SCROLL_PAGE_UP);
-
-  gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
-                               "scroll_text", 1,
-                               GTK_TYPE_ENUM, GTK_TEXT_SCROLL_PAGE_DOWN);
-
   /* Deleting text */
   gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
-                               "delete_text", 2,
-                               GTK_TYPE_ENUM, GTK_TEXT_DELETE_CHAR,
+                               "delete", 2,
+                               GTK_TYPE_ENUM, GTK_DELETE_CHARS,
+                               GTK_TYPE_INT, 1);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_CONTROL_MASK,
+                               "delete", 2,
+                               GTK_TYPE_ENUM, GTK_DELETE_CHARS,
                                GTK_TYPE_INT, 1);
 
   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
-                               "delete_text", 2,
-                               GTK_TYPE_ENUM, GTK_TEXT_DELETE_CHAR,
+                               "delete", 2,
+                               GTK_TYPE_ENUM, GTK_DELETE_CHARS,
                                GTK_TYPE_INT, -1);
 
   gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
-                               "delete_text", 2,
-                               GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_WORD,
+                               "delete", 2,
+                               GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
+                               GTK_TYPE_INT, 1);
+
+  gtk_binding_entry_add_signal (binding_set, GDK_d, GDK_MOD1_MASK,
+                               "delete", 2,
+                               GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
                                GTK_TYPE_INT, 1);
 
   gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
-                               "delete_text", 2,
-                               GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_WORD,
+                               "delete", 2,
+                               GTK_TYPE_ENUM, GTK_DELETE_WORD_ENDS,
                                GTK_TYPE_INT, -1);
 
   gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
-                               "delete_text", 2,
-                               GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_LINE,
+                               "delete", 2,
+                               GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPH_ENDS,
                                GTK_TYPE_INT, 1);
 
   gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
-                               "delete_text", 2,
-                               GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHOLE_LINE,
+                               "delete", 2,
+                               GTK_TYPE_ENUM, GTK_DELETE_PARAGRAPHS,
                                GTK_TYPE_INT, 1);
 
   gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
-                               "delete_text", 2,
-                               GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE,
+                               "delete", 2,
+                               GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
                                GTK_TYPE_INT, 1);
+  gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
+                               "insert", 1,
+                               GTK_TYPE_STRING, " ");
 
   gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
-                               "delete_text", 2,
-                               GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHITESPACE,
+                               "delete", 2,
+                               GTK_TYPE_ENUM, GTK_DELETE_WHITESPACE,
                                GTK_TYPE_INT, 1);
   
   /* Cut/copy/paste */
 
   gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
-                               "cut_text", 0);
+                               "cut_clipboard", 0);
 
   gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
-                               "cut_text", 0);
+                               "cut_clipboard", 0);
   
   gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
-                               "copy_text", 0);
+                               "copy_clipboard", 0);
   
+  gtk_binding_entry_add_signal (binding_set, GDK_v, GDK_CONTROL_MASK,
+                               "paste_clipboard", 0);
+
   gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
-                               "paste_text", 0);
+                               "paste_clipboard", 0);
 
   /* Overwrite */
   gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
@@ -570,13 +582,13 @@ gtk_text_view_class_init (GtkTextViewClass *klass)
   widget_class->drag_drop = gtk_text_view_drag_drop;
   widget_class->drag_data_received = gtk_text_view_drag_data_received;
 
-  klass->move_insert = gtk_text_view_move_insert;
+  klass->move = gtk_text_view_move;
   klass->set_anchor = gtk_text_view_set_anchor;
-  klass->scroll_text = gtk_text_view_scroll_text;
-  klass->delete_text = gtk_text_view_delete_text;
-  klass->cut_text = gtk_text_view_cut_text;
-  klass->copy_text = gtk_text_view_copy_text;
-  klass->paste_text = gtk_text_view_paste_text;
+  klass->insert = gtk_text_view_insert;
+  klass->delete = gtk_text_view_delete;
+  klass->cut_clipboard = gtk_text_view_cut_clipboard;
+  klass->copy_clipboard = gtk_text_view_copy_clipboard;
+  klass->paste_clipboard = gtk_text_view_paste_clipboard;
   klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
   klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments;
 }
@@ -592,21 +604,9 @@ gtk_text_view_init (GtkTextView *text_view)
 
   text_view->wrap_mode = GTK_WRAPMODE_NONE;
 
-  if (!clipboard_atom)
-    clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
-
-  if (!text_atom)
-    text_atom = gdk_atom_intern ("TEXT", FALSE);
-
-  if (!ctext_atom)
-    ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
-
-  if (!utf8_atom)
-    utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
-  
   gtk_drag_dest_set (widget,
                     GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION,
-                    target_table, n_targets,
+                    target_table, G_N_ELEMENTS (target_table),
                     GDK_ACTION_COPY | GDK_ACTION_MOVE);
 
   text_view->virtual_cursor_x = -1;
@@ -1667,11 +1667,9 @@ gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
                                              event->x + text_view->xoffset,
                                              event->y + text_view->yoffset);
           
-          gtk_text_buffer_paste_primary_selection (text_view->buffer,
-                                                   &iter,
-                                                   event->time,
-                                                   TRUE,
-                                                   text_view->editable);
+          gtk_text_buffer_paste_primary (text_view->buffer,
+                                        &iter,
+                                        text_view->editable);
           return TRUE;
         }
       else if (event->button == 3)
@@ -1843,61 +1841,77 @@ gtk_text_view_move_iter_by_lines (GtkTextView *text_view,
 }
 
 static void
-gtk_text_view_move_insert (GtkTextView *text_view,
-                          GtkTextViewMovementStep step,
-                          gint count,
-                          gboolean extend_selection)
+gtk_text_view_move (GtkTextView     *text_view,
+                   GtkMovementStep  step,
+                   gint             count,
+                   gboolean         extend_selection)
 {
   GtkTextIter insert;
   GtkTextIter newplace;
   
   gint cursor_x_pos = 0;
 
+  if (step == GTK_MOVEMENT_PAGES)
+    {
+      gtk_text_view_scroll_pages (text_view, count);
+      return;
+    }
+  
   gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert,
                                     gtk_text_buffer_get_mark (text_view->buffer,
                                                               "insert"));
   newplace = insert;
 
-  if (step == GTK_TEXT_MOVEMENT_WRAPPED_LINE)
+  if (step == GTK_MOVEMENT_DISPLAY_LINES)
     gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, NULL);
 
   switch (step)
     {
-    case GTK_TEXT_MOVEMENT_CHAR:
+    case GTK_MOVEMENT_CHARS:
       gtk_text_iter_forward_chars (&newplace, count);
       break;
 
-    case GTK_TEXT_MOVEMENT_POSITIONS:
+    case GTK_MOVEMENT_POSITIONS:
       gtk_text_layout_move_iter_visually (text_view->layout,
                                          &newplace, count);
       break;
 
-    case GTK_TEXT_MOVEMENT_WORD:
+    case GTK_MOVEMENT_WORDS:
       if (count < 0)
        gtk_text_iter_backward_word_starts (&newplace, -count);
       else if (count > 0)
        gtk_text_iter_forward_word_ends (&newplace, count);
       break;
 
-    case GTK_TEXT_MOVEMENT_WRAPPED_LINE:
+    case GTK_MOVEMENT_DISPLAY_LINES:
       gtk_text_view_move_iter_by_lines (text_view, &newplace, count);
       gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
       break;
       
-    case GTK_TEXT_MOVEMENT_LINE:
+    case GTK_MOVEMENT_DISPLAY_LINE_ENDS:
+      if (count > 1)
+       gtk_text_view_move_iter_by_lines (text_view, &newplace, --count);
+      else if (count < -1)
+       gtk_text_view_move_iter_by_lines (text_view, &newplace, ++count);
+
+      if (count != 0)
+       gtk_text_layout_move_iter_to_line_end (text_view->layout, &newplace, count);
+      break;
+      
+    case GTK_MOVEMENT_PARAGRAPHS:
       /* This should almost certainly instead be doing the parallel thing to WORD */
       /*       gtk_text_iter_down_lines (&newplace, count); */
       /* FIXME */
       break;
       
-    case GTK_TEXT_MOVEMENT_LINE_ENDS:
+    case GTK_MOVEMENT_PARAGRAPH_ENDS:
       if (count > 0)
        gtk_text_iter_forward_to_newline (&newplace);
       else if (count < 0)
        gtk_text_iter_set_line_offset (&newplace, 0);
       break;
       
-    case GTK_TEXT_MOVEMENT_BUFFER_ENDS:
+    case GTK_MOVEMENT_BUFFER_ENDS:
       if (count > 0)
        gtk_text_buffer_get_last_iter (text_view->buffer, &newplace);
       else if (count < 0)
@@ -1922,7 +1936,7 @@ gtk_text_view_move_insert (GtkTextView *text_view,
                                     gtk_text_buffer_get_mark (text_view->buffer,
                                                               "insert"), 0);
 
-      if (step == GTK_TEXT_MOVEMENT_WRAPPED_LINE)
+      if (step == GTK_MOVEMENT_DISPLAY_LINES)
        {
          gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
        }
@@ -1942,8 +1956,8 @@ gtk_text_view_set_anchor (GtkTextView *text_view)
 }
 
 static void
-gtk_text_view_scroll_text (GtkTextView *text_view,
-                          GtkTextViewScrollType type)
+gtk_text_view_scroll_pages (GtkTextView *text_view,
+                           gint         count)
 {
   gfloat newval;
   GtkAdjustment *adj;
@@ -1958,60 +1972,26 @@ gtk_text_view_scroll_text (GtkTextView *text_view,
 
   /* Validate the region that will be brought into view by the cursor motion
    */
-  switch (type)
+  if (count < 0)
     {
-    default:
-    case GTK_TEXT_SCROLL_TO_TOP:
-      gtk_text_buffer_get_iter_at_offset (text_view->buffer, &anchor, 0);
-      y0 = 0;
-      y1 = adj->page_size;
-      break;
-
-    case GTK_TEXT_SCROLL_TO_BOTTOM:
-      gtk_text_buffer_get_last_iter (text_view->buffer, &anchor);
-      y0 = -adj->page_size;
-      y1 = adj->page_size;
-      break;
-
-    case GTK_TEXT_SCROLL_PAGE_DOWN:
       gtk_text_view_get_first_para_iter (text_view, &anchor);
       y0 = adj->page_size;
-      y1 = adj->page_size + adj->page_increment;
-      break;
-
-    case GTK_TEXT_SCROLL_PAGE_UP:
+      y1 = adj->page_size + count * adj->page_increment;
+    }
+  else
+    {
       gtk_text_view_get_first_para_iter (text_view, &anchor);
-      y0 = - adj->page_increment + adj->page_size;
+      y0 = count * adj->page_increment + adj->page_size;
       y1 = 0;
-      break;
     }
+  
   gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1);
 
 
   gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
 
   newval = adj->value;
-  switch (type)
-    {
-    case GTK_TEXT_SCROLL_TO_TOP:
-      newval = adj->lower;
-      break;
-
-    case GTK_TEXT_SCROLL_TO_BOTTOM:
-      newval = adj->upper;
-      break;
-
-    case GTK_TEXT_SCROLL_PAGE_DOWN:
-      newval += adj->page_increment;
-      break;
-
-    case GTK_TEXT_SCROLL_PAGE_UP:
-      newval -= adj->page_increment;
-      break;
-      
-    default:
-      break;
-    }
+  newval += count * adj->page_increment;
 
   cursor_y_pos += newval - adj->value;
   set_adjustment_clamped (adj, newval);
@@ -2059,16 +2039,24 @@ find_whitepace_region (const GtkTextIter *center,
 }
 
 static void
-gtk_text_view_delete_text (GtkTextView *text_view,
-                       GtkTextViewDeleteType type,
-                       gint count)
+gtk_text_view_insert (GtkTextView *text_view,
+                     const gchar *str)
+{
+  gtk_text_buffer_insert_interactive_at_cursor (text_view->buffer, str, -1,
+                                               text_view->editable);
+}
+
+static void
+gtk_text_view_delete (GtkTextView   *text_view,
+                          GtkDeleteType  type,
+                          gint           count)
 {
   GtkTextIter insert;
   GtkTextIter start;
   GtkTextIter end;
   gboolean leave_one = FALSE;
   
-  if (type == GTK_TEXT_DELETE_CHAR)
+  if (type == GTK_DELETE_CHARS)
     {
       /* Char delete deletes the selection, if one exists */
       if (gtk_text_buffer_delete_selection (text_view->buffer, TRUE,
@@ -2086,27 +2074,27 @@ gtk_text_view_delete_text (GtkTextView *text_view,
   
   switch (type)
     {
-    case GTK_TEXT_DELETE_CHAR:
+    case GTK_DELETE_CHARS:
       gtk_text_iter_forward_chars (&end, count);
       break;
       
-    case GTK_TEXT_DELETE_HALF_WORD:
+    case GTK_DELETE_WORD_ENDS:
       if (count > 0)
         gtk_text_iter_forward_word_ends (&end, count);
       else if (count < 0)
         gtk_text_iter_backward_word_starts (&start, 0 - count);
       break;
       
-    case GTK_TEXT_DELETE_WHOLE_WORD:
+    case GTK_DELETE_WORDS:
       break;
       
-    case GTK_TEXT_DELETE_HALF_WRAPPED_LINE:
+    case GTK_DELETE_DISPLAY_LINE_ENDS:
       break;
 
-    case GTK_TEXT_DELETE_WHOLE_WRAPPED_LINE:
+    case GTK_DELETE_DISPLAY_LINES:
       break;
 
-    case GTK_TEXT_DELETE_HALF_LINE:
+    case GTK_DELETE_PARAGRAPH_ENDS:
       while (count > 0)
         {
           if (!gtk_text_iter_forward_to_newline (&end))
@@ -2119,7 +2107,7 @@ gtk_text_view_delete_text (GtkTextView *text_view,
          and support that */
       break;
       
-    case GTK_TEXT_DELETE_WHOLE_LINE:
+    case GTK_DELETE_PARAGRAPHS:
       if (count > 0)
         {
           gtk_text_iter_set_line_offset (&start, 0);
@@ -2138,9 +2126,7 @@ gtk_text_view_delete_text (GtkTextView *text_view,
       
       break;
 
-    case GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE:
-      leave_one = TRUE; /* FALL THRU */
-    case GTK_TEXT_DELETE_WHITESPACE:
+    case GTK_DELETE_WHITESPACE:
       {
         find_whitepace_region (&insert, &start, &end);
       }
@@ -2168,9 +2154,9 @@ gtk_text_view_delete_text (GtkTextView *text_view,
 }
 
 static void
-gtk_text_view_cut_text (GtkTextView *text_view)
+gtk_text_view_cut_clipboard (GtkTextView *text_view)
 {
-  gtk_text_buffer_cut (text_view->buffer, GDK_CURRENT_TIME, TRUE, text_view->editable);
+  gtk_text_buffer_cut_clipboard (text_view->buffer, text_view->editable);
   gtk_text_view_scroll_to_mark (text_view,
                                 gtk_text_buffer_get_mark (text_view->buffer,
                                                           "insert"),
@@ -2178,9 +2164,9 @@ gtk_text_view_cut_text (GtkTextView *text_view)
 }
 
 static void
-gtk_text_view_copy_text (GtkTextView *text_view)
+gtk_text_view_copy_clipboard (GtkTextView *text_view)
 {
-  gtk_text_buffer_copy (text_view->buffer, GDK_CURRENT_TIME);
+  gtk_text_buffer_copy_clipboard (text_view->buffer);
   gtk_text_view_scroll_to_mark (text_view,
                                 gtk_text_buffer_get_mark (text_view->buffer,
                                                           "insert"),
@@ -2188,9 +2174,9 @@ gtk_text_view_copy_text (GtkTextView *text_view)
 }
 
 static void
-gtk_text_view_paste_text (GtkTextView *text_view)
+gtk_text_view_paste_clipboard (GtkTextView *text_view)
 {
-  gtk_text_buffer_paste_clipboard (text_view->buffer, GDK_CURRENT_TIME, TRUE, text_view->editable);
+  gtk_text_buffer_paste_clipboard (text_view->buffer, text_view->editable);
   gtk_text_view_scroll_to_mark (text_view,
                                 gtk_text_buffer_get_mark (text_view->buffer,
                                                           "insert"),
@@ -2567,16 +2553,14 @@ gtk_text_view_start_selection_dnd (GtkTextView *text_view,
   GdkDragContext *context;
   GtkTargetList *target_list;
   
-  /* FIXME we have to handle more formats for the selection,
-     and do the conversions to/from UTF8 */
-  
-  /* FIXME not sure how this is memory-managed. */
-  target_list = gtk_target_list_new (target_table, n_targets);
-  
+  target_list = gtk_target_list_new (target_table, G_N_ELEMENTS (target_table));
+
   context = gtk_drag_begin (GTK_WIDGET (text_view), target_list,
                            GDK_ACTION_COPY | GDK_ACTION_MOVE,
                            1, (GdkEvent*)event);
 
+  gtk_target_list_unref (target_list);
+
   gtk_drag_set_icon_default (context);
 
   /* We're inside the selection, so start without being able
@@ -2631,46 +2615,7 @@ gtk_text_view_drag_data_get (GtkWidget        *widget,
 
   if (str)
     {
-      if (info == TARGET_UTF8_STRING)
-        {
-          /* Pass raw UTF8 */
-          gtk_selection_data_set (selection_data,
-                                  utf8_atom,
-                                  8*sizeof (gchar), (guchar *)str, length);
-
-        }
-      else if (info == TARGET_STRING ||
-               info == TARGET_TEXT)
-        {
-          gchar *latin1;
-
-          latin1 = gtk_text_utf_to_latin1(str, length);
-          
-          gtk_selection_data_set (selection_data,
-                                  GDK_SELECTION_TYPE_STRING,
-                                  8*sizeof (gchar), latin1, strlen (latin1));
-          g_free (latin1);
-        }
-      else if (info == TARGET_COMPOUND_TEXT)
-        {
-          /* FIXME convert UTF8 directly to current locale, not via
-             latin1 */
-          
-          guchar *text;
-          GdkAtom encoding;
-          gint format;
-          gint new_length;
-          gchar *latin1;
-
-          latin1 = gtk_text_utf_to_latin1(str, length);
-          
-          gdk_string_to_compound_text (latin1, &encoding, &format, &text, &new_length);
-          gtk_selection_data_set (selection_data, encoding, format, text, new_length);
-          gdk_free_compound_text (text);
-
-          g_free (latin1);
-        }
-
+      gtk_selection_data_set_text (selection_data, str);
       g_free (str);
     }
 }
@@ -2792,27 +2737,10 @@ gtk_text_view_drag_data_received (GtkWidget        *widget,
   GtkTextIter drop_point;
   GtkTextView *text_view;
   GtkTextMark *drag_target_mark;
+  gchar *str;
   
-  enum {INVALID, STRING, CTEXT, UTF8} type;
-
   text_view = GTK_TEXT_VIEW (widget);
   
-  if (selection_data->type == GDK_TARGET_STRING)
-    type = STRING;
-  else if (selection_data->type == ctext_atom)
-    type = CTEXT;
-  else if (selection_data->type == utf8_atom)
-    type = UTF8;
-  else
-    type = INVALID;
-
-  if (type == INVALID || selection_data->length < 0)
-    {
-      /* I think the DND code automatically tries asking
-         for the various formats. */
-      return;
-    }
-
   drag_target_mark = gtk_text_buffer_get_mark (text_view->buffer,
                                                "__drag_target");
   
@@ -2823,71 +2751,13 @@ gtk_text_view_drag_data_received (GtkWidget        *widget,
                                     &drop_point,
                                     drag_target_mark);
 
-  
-  switch (type)
+  str = gtk_selection_data_get_text (selection_data);
+  if (str)
     {
-    case STRING:
-      {
-        gchar *utf;
-
-        utf = gtk_text_latin1_to_utf ((const gchar*)selection_data->data,
-                                      selection_data->length);
-        gtk_text_buffer_insert_interactive (text_view->buffer, &drop_point,
-                                            utf, -1,
-                                            text_view->editable);
-        g_free (utf);
-      }
-      break;
-      
-    case UTF8:
-      gtk_text_buffer_insert_interactive (text_view->buffer, &drop_point,
-                                          (const gchar *)selection_data->data,
-                                          selection_data->length,
-                                          text_view->editable);
-      break;
-      
-    case CTEXT:
-      {
-       gchar **list;
-       gint count;
-       gint i;
-
-       count = gdk_text_property_to_text_list (selection_data->type,
-                                               selection_data->format, 
-                                               selection_data->data,
-                                               selection_data->length,
-                                               &list);
-       for (i=0; i<count; i++)
-          {
-            /* list contains stuff in our default encoding */
-            gboolean free_utf = FALSE;
-            gchar *utf = NULL;
-            gchar *charset = NULL;
-            
-            if (g_get_charset (&charset))
-              {
-                utf = g_convert (list[i], -1,
-                                 "UTF8", charset, NULL);
-                free_utf = TRUE;
-              }
-            else
-              utf = list[i];
-
-            gtk_text_buffer_insert_interactive (text_view->buffer,
-                                                &drop_point, utf, -1,
-                                                text_view->editable);
-
-            if (free_utf)
-              g_free(utf);
-          }
-
-       if (count > 0)
-         gdk_free_text_list (list);
-      }
-      break;
-      
-    case INVALID:              /* quiet compiler */
-      break;
+      gtk_text_buffer_insert_interactive (text_view->buffer,
+                                         &drop_point, str, -1,
+                                         text_view->editable);
+      g_free (str);
     }
 }
 
@@ -3029,7 +2899,7 @@ gtk_text_view_commit_handler (GtkIMContext  *context,
   else
     {
       if (text_view->overwrite_mode)
-       gtk_text_view_delete_text (text_view, GTK_TEXT_DELETE_CHAR, 1);
+       gtk_text_view_delete (text_view, GTK_DELETE_CHARS, 1);
       gtk_text_buffer_insert_interactive_at_cursor (text_view->buffer, str, -1,
                                                     text_view->editable);
     }
index f20c2f0620184f3bb09ec784c17b6b553d54cff7..1b32c0386d805fa398f6ec58524124d722af4558 100644 (file)
@@ -9,37 +9,6 @@
 extern "C" {
 #endif /* __cplusplus */
 
-typedef enum {
-  GTK_TEXT_MOVEMENT_CHAR,       /* move by forw/back chars */
-  GTK_TEXT_MOVEMENT_POSITIONS,  /* move by left/right chars */
-  GTK_TEXT_MOVEMENT_WORD,       /* move by forward/back words */
-  GTK_TEXT_MOVEMENT_WRAPPED_LINE,       /* move up/down lines (wrapped lines) */
-  GTK_TEXT_MOVEMENT_LINE,  /* move up/down paragraphs (newline-ended lines) */
-  GTK_TEXT_MOVEMENT_LINE_ENDS,   /* move to either end of a paragraph */
-  GTK_TEXT_MOVEMENT_BUFFER_ENDS       /* move to ends of the buffer */
-} GtkTextViewMovementStep;
-
-typedef enum {
-  GTK_TEXT_SCROLL_TO_TOP,
-  GTK_TEXT_SCROLL_TO_BOTTOM,
-  GTK_TEXT_SCROLL_PAGE_DOWN,
-  GTK_TEXT_SCROLL_PAGE_UP
-} GtkTextViewScrollType;
-
-typedef enum {
-  GTK_TEXT_DELETE_CHAR,
-  GTK_TEXT_DELETE_HALF_WORD, /* delete only the portion of the word to the
-                                 left/right of cursor if we're in the middle
-                                 of a word */
-  GTK_TEXT_DELETE_WHOLE_WORD,
-  GTK_TEXT_DELETE_HALF_WRAPPED_LINE,
-  GTK_TEXT_DELETE_WHOLE_WRAPPED_LINE,
-  GTK_TEXT_DELETE_HALF_LINE,  /* like C-k in Emacs (or its reverse) */
-  GTK_TEXT_DELETE_WHOLE_LINE, /* C-k in pico, kill whole line */
-  GTK_TEXT_DELETE_WHITESPACE,      /* M-\ in Emacs */
-  GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE /* M-space in Emacs */
-} GtkTextViewDeleteType;
-
 #define GTK_TYPE_TEXT_VIEW             (gtk_text_view_get_type())
 #define GTK_TEXT_VIEW(obj)             (GTK_CHECK_CAST ((obj), GTK_TYPE_TEXT_VIEW, GtkTextView))
 #define GTK_TEXT_VIEW_CLASS(klass)     (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_VIEW, GtkTextViewClass))
@@ -107,17 +76,16 @@ struct _GtkTextViewClass {
   /* These are all RUN_ACTION signals for keybindings */
 
   /* move insertion point */
-  void (* move_insert) (GtkTextView *text_view, GtkTextViewMovementStep step, gint count, gboolean extend_selection);
+  void (* move)        (GtkTextView *text_view, GtkMovementStep step, gint count, gboolean extend_selection);
   /* move the "anchor" (what Emacs calls the mark) to the cursor position */
   void (* set_anchor)  (GtkTextView *text_view);
-  /* Scroll */
-  void (* scroll_text) (GtkTextView *text_view, GtkTextViewScrollType type);
   /* Deletions */
-  void (* delete_text) (GtkTextView *text_view, GtkTextViewDeleteType type, gint count);
+  void (* insert)      (GtkTextView *text_view, const gchar *str);
+  void (* delete)      (GtkTextView *text_view, GtkDeleteType type, gint count);
   /* cut copy paste */
-  void (* cut_text)    (GtkTextView *text_view);
-  void (* copy_text)    (GtkTextView *text_view);
-  void (* paste_text)    (GtkTextView *text_view);
+  void (* cut_clipboard)   (GtkTextView *text_view);
+  void (* copy_clipboard)  (GtkTextView *text_view);
+  void (* paste_clipboard) (GtkTextView *text_view);
   /* overwrite */
   void (* toggle_overwrite) (GtkTextView *text_view);
   void  (*set_scroll_adjustments)   (GtkTextView    *text_view,